aboutsummaryrefslogtreecommitdiff
path: root/rp/config
diff options
context:
space:
mode:
Diffstat (limited to 'rp/config')
-rw-r--r--rp/config/Makefile.in88
l---------rp/config/rpki1
-rwxr-xr-xrp/config/rpki-confgen281
-rw-r--r--rp/config/rpki-confgen.xml1062
-rwxr-xr-xrp/config/rpki-generate-root-certificate77
-rwxr-xr-xrp/config/rpki-manage46
-rwxr-xr-xrp/config/rpki-sql-backup63
-rwxr-xr-xrp/config/rpki-sql-setup348
8 files changed, 1966 insertions, 0 deletions
diff --git a/rp/config/Makefile.in b/rp/config/Makefile.in
new file mode 100644
index 00000000..c6050f3e
--- /dev/null
+++ b/rp/config/Makefile.in
@@ -0,0 +1,88 @@
+# $Id$
+
+PYTHON = @PYTHON@
+
+INSTALL = @INSTALL@ -m 555
+
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+datarootdir = @datarootdir@
+datadir = @datadir@
+localstatedir = @localstatedir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+sysconfdir = @sysconfdir@
+
+abs_builddir = @abs_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+abs_top_builddir= @abs_top_builddir@
+srcdir = @srcdir@
+
+CFG_INSTALL_TARGETS = @CFG_INSTALL_TARGETS@
+
+all:: rpki.rp.xml rpki.rp.conf.sample
+
+clean::
+ @true
+
+install:: ${CFG_INSTALL_TARGETS}
+
+install-always:: all
+ @echo
+ @echo "== Default configuration file location is ${sysconfdir}/rpki.conf =="
+ @echo
+ ${INSTALL} -d ${DESTDIR}${sysconfdir}/rpki
+ ${INSTALL} rpki.rp.xml rpki.rp.conf.sample ${DESTDIR}${sysconfdir}/rpki
+
+test uninstall deinstall::
+ @true
+
+distclean:: clean
+ rm -f Makefile
+
+rpki.rp.xml: ${abs_top_srcdir}/rpki/autoconf.py rpki-confgen rpki-confgen.xml
+ ${PYTHON} rpki-confgen \
+ --read-xml rpki-confgen.xml \
+ --autoconf \
+ --set myrpki::handle=`hostname -f | sed 's/[.]/_/g'` \
+ --set myrpki::rpkid_server_host=`hostname -f` \
+ --set myrpki::pubd_server_host=`hostname -f` \
+ --pwgen myrpki::shared_sql_password \
+ --pwgen web_portal::secret-key \
+ --set myrpki::run_rpkid=no \
+ --set myrpki::run_pubd=no \
+ --write-xml $@
+
+rpki.rp.conf.sample: rpki.rp.xml
+ ${PYTHON} rpki-confgen \
+ --read-xml rpki.rp.xml \
+ --write-conf $@
+
+clean::
+ rm -f rpki.rp.xml rpki.rp.conf.sample
+
+install-postconf: \
+ install-user install-conf install-sql install-django
+
+# This should create user "rpki" and group "rpki", but rcynic already
+# does that...but we probably need to do it here instead, bother.
+
+install-user:
+ @true
+
+install-conf:
+ test -f ${DESTDIR}${sysconfdir}/rpki.conf ||\
+ cp -p ${DESTDIR}${sysconfdir}/rpki/rpki.rp.conf.sample ${DESTDIR}${sysconfdir}/rpki.conf
+
+#uninstall deinstall::
+# rm -f ${DESTDIR}${sysconfdir}/rpki/rpki.rp.xml ${DESTDIR}${sysconfdir}/rpki/rpki.rp.conf.sample
+
+install-sql:
+ ${sbindir}/rpki-sql-setup create
+
+install-django:
+ ${sbindir}/rpki-manage syncdb --noinput
+ ${sbindir}/rpki-manage migrate app
diff --git a/rp/config/rpki b/rp/config/rpki
new file mode 120000
index 00000000..d39d05b6
--- /dev/null
+++ b/rp/config/rpki
@@ -0,0 +1 @@
+../../rpki \ No newline at end of file
diff --git a/rp/config/rpki-confgen b/rp/config/rpki-confgen
new file mode 100755
index 00000000..7fac9eab
--- /dev/null
+++ b/rp/config/rpki-confgen
@@ -0,0 +1,281 @@
+#!/usr/bin/env python
+
+# $Id$
+#
+# Copyright (C) 2014 Dragon Research Labs ("DRL")
+# Portions copyright (C) 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 sys
+import argparse
+import base64
+import textwrap
+
+from lxml.etree import Element, SubElement, ElementTree, Comment
+
+space4 = " " * 4
+space6 = " " * 6
+space8 = " " * 8
+star78 = "*" * 78
+
+wiki_wrapper = textwrap.TextWrapper()
+conf_wrapper = textwrap.TextWrapper(initial_indent = "# ", subsequent_indent = "# ")
+xml6_wrapper = textwrap.TextWrapper(initial_indent = space6, subsequent_indent = space6)
+xml8_wrapper = textwrap.TextWrapper(initial_indent = space8, subsequent_indent = space8)
+
+class Option(object):
+
+ def __init__(self, name, value, doc):
+ self.name = name
+ self.value = value
+ self.doc = doc
+
+ @property
+ def width(self):
+ return len(self.name)
+
+ def to_xml(self):
+ x = Element("option", name = self.name)
+ if self.value is not None:
+ x.set("value", self.value)
+ for d in self.doc:
+ SubElement(x, "doc").text = "\n" + xml8_wrapper.fill(d) + "\n" + space6
+ return x
+
+ def to_wiki(self, f):
+ f.write("\n== {0.name} == #{0.name}\n".format(self))
+ for d in self.doc:
+ f.write("\n{0}\n".format(wiki_wrapper.fill(d)))
+ if self.value is None:
+ f.write("\n{0}\n".format(wiki_wrapper.fill("No default value.")))
+ else:
+ f.write("\n{{{{{{\n#!ini\n{0.name} = {0.value}\n}}}}}}\n".format(self))
+
+ def to_conf(self, f, width):
+ for i, d in enumerate(self.doc):
+ f.write("{}\n{}\n".format("" if i == 0 else "#",
+ conf_wrapper.fill(d)))
+ if self.value is None:
+ f.write("\n#{1.name:{0}} = ???\n".format(width - 1, self))
+ else:
+ f.write("\n{1.name:{0}} = {1.value}\n".format(width, self))
+
+class Section(object):
+
+ def __init__(self, name):
+ self.name = name
+ self.doc = []
+ self.options = []
+
+ @property
+ def width(self):
+ return max(o.width for o in self.options)
+
+ @classmethod
+ def from_xml(cls, elt):
+ self = cls(name = elt.get("name"))
+ for x in elt.iterchildren("doc"):
+ self.doc.append(" ".join(x.text.split()))
+ for x in elt.iterchildren("option"):
+ self.options.append(Option(name = x.get("name"), value = x.get("value"),
+ doc = [" ".join(d.text.split())
+ for d in x.iterchildren("doc")]))
+ return self
+
+ def to_xml(self):
+ x = Element("section", name = self.name)
+ for d in self.doc:
+ SubElement(x, "doc").text = "\n" + xml6_wrapper.fill(d) + "\n" + space4
+ x.extend(o.to_xml() for o in self.options)
+ return x
+
+ def to_wiki(self, f):
+ f.write("\n= [{0}] section = #{0}\n".format(self.name))
+ for d in self.doc:
+ f.write("\n{0}\n".format(wiki_wrapper.fill(d)))
+ for o in self.options:
+ o.to_wiki(f)
+
+ def to_conf(self, f, width):
+ f.write("\n" + "#" * 78 + "\n\n[" + self.name + "]\n")
+ if self.doc:
+ f.write("\n##")
+ for i, d in enumerate(self.doc):
+ f.write("{}\n{}\n".format("" if i == 0 else "#",
+ conf_wrapper.fill(d)))
+ f.write("##\n")
+ for o in self.options:
+ o.to_conf(f, width)
+
+def wiki_header(f, ident, toc):
+ f.write(textwrap.dedent('''\
+ {{{{{{
+ #!comment
+
+ {star78}
+ THIS PAGE WAS GENERATED AUTOMATICALLY, DO NOT EDIT.
+
+ Generated from {ident}
+ by $Id$
+ {star78}
+
+ }}}}}}
+ '''.format(star78 = star78,
+ ident = ident)))
+ if toc is not None:
+ f.write("[[TracNav({})]]\n".format(toc))
+ f.write("[[PageOutline]]\n")
+
+def conf_header(f, ident):
+ f.write(textwrap.dedent('''\
+ # Automatically generated. Edit as needed, but be careful of overwriting.
+ #
+ # Generated from {ident}
+ # by $Id$
+
+ '''.format(ident = ident)))
+
+
+# http://stackoverflow.com/questions/9027028/argparse-argument-order
+
+class CustomAction(argparse.Action):
+
+ def __call__(self, parser, namespace, values, option_string = None):
+ if not "ordered_args" in namespace:
+ namespace.ordered_args = []
+ namespace.ordered_args.append((self.dest, values))
+
+class CustomFlagAction(CustomAction):
+
+ def __init__(self, option_strings, dest, default = None,
+ required = False, help = None): # pylint: disable=W0622
+ super(CustomFlagAction, self).__init__(
+ option_strings = option_strings,
+ dest = dest,
+ nargs = 0,
+ const = None,
+ default = default,
+ required = required,
+ help = help)
+
+
+class main(object):
+
+ def __init__(self):
+ self.sections = []
+ self.section_map = None
+ self.option_map = None
+ self.ident = None
+ self.toc = None
+
+ parser = argparse.ArgumentParser(description = __doc__)
+ parser.add_argument("--read-xml", type = argparse.FileType("r"), metavar = "FILE", action = CustomAction, help = "XML input file defining sections and options", required = True)
+ parser.add_argument("--write-xml", type = argparse.FileType("w"), metavar = "FILE", action = CustomAction, help = "XML output file to snapshot configuration")
+ parser.add_argument("--write-conf", type = argparse.FileType("w"), metavar = "FILE", action = CustomAction, help = "rpki.conf configuration file to write")
+ parser.add_argument("--write-wiki", type = argparse.FileType("w"), metavar = "FILE", action = CustomAction, help = "TracWiki file to write (monolithic)")
+ parser.add_argument("--write-wiki-pages", metavar = "PATTERN", action = CustomAction, help = "TracWiki filenames (pattern) to write (one section per page)")
+ parser.add_argument("--set", metavar = "VARVAL", action = CustomAction, help = "variable setting in form \"VAR=VAL\"")
+ parser.add_argument("--pwgen", metavar = "VAR", action = CustomAction, help = "set variable to generated password")
+ parser.add_argument("--toc", metavar = "TOCVAL", action = CustomAction, help = "set TOC value to use with TracNav plugin")
+ parser.add_argument("--autoconf", action = CustomFlagAction, help = "configure [autoconf] section")
+ args = parser.parse_args()
+
+ for cmd, arg in args.ordered_args:
+ getattr(self, "do_" + cmd)(arg)
+
+ def do_read_xml(self, arg):
+ self.option_map = None
+ root = ElementTree(file = arg).getroot()
+ self.ident = root.get("ident")
+ self.sections.extend(Section.from_xml(x) for x in root.iterchildren("section"))
+ self.option_map = {}
+ self.section_map = {}
+ for section in self.sections:
+ if section.name in self.section_map:
+ sys.exit("Duplicate section {}".format(section.name))
+ self.section_map[section.name] = section
+ for option in section.options:
+ name = (section.name, option.name)
+ if name in self.option_map:
+ sys.exit("Duplicate option {}::{}".format(*name))
+ self.option_map[name] = option
+
+ def do_set(self, arg):
+ try:
+ name, value = arg.split("=", 1)
+ section, option = name.split("::")
+ except ValueError:
+ sys.exit("Couldn't parse --set specification \"{}\"".format(arg))
+ name = (section, option)
+ if name not in self.option_map:
+ sys.exit("Couldn't find option {}::{}".format(*name))
+ self.option_map[name].value = value
+
+ def do_pwgen(self, arg):
+ try:
+ section, option = arg.split("::")
+ except ValueError:
+ sys.exit("Couldn't parse --pwgen specification \"{}\"".format(arg))
+ name = (section, option)
+ if name not in self.option_map:
+ sys.exit("Couldn't find option {}::{}".format(name))
+ self.option_map[name].value = base64.urlsafe_b64encode(os.urandom(66))
+
+ def do_autoconf(self, ignored):
+ try:
+ import rpki.autoconf
+ for option in self.section_map["autoconf"].options:
+ try:
+ option.value = getattr(rpki.autoconf, option.name)
+ except AttributeError:
+ pass
+ except ImportError:
+ sys.exit("rpki.autoconf module is not available")
+ except KeyError:
+ sys.exit("Couldn't find autoconf section")
+
+ def do_write_xml(self, arg):
+ x = Element("configuration", ident = self.ident)
+ x.append(Comment(" Machine-editable configuration snapshot, generated automatically, do not touch "))
+ x.extend(s.to_xml() for s in self.sections)
+ ElementTree(x).write(arg, pretty_print = True, encoding = "us-ascii")
+
+ def do_write_wiki(self, arg):
+ for i, section in enumerate(self.sections):
+ if i == 0:
+ wiki_header(arg, self.ident, self.toc)
+ else:
+ arg.write("\f\n")
+ section.to_wiki(arg)
+
+ def do_write_wiki_pages(self, arg):
+ for section in self.sections:
+ with open(arg % section.name, "w") as f:
+ wiki_header(f, self.ident, self.toc)
+ section.to_wiki(f)
+
+ def do_write_conf(self, arg):
+ conf_header(arg, self.ident)
+ width = max(s.width for s in self.sections)
+ for section in self.sections:
+ section.to_conf(arg, width)
+
+ def do_toc(self, arg):
+ self.toc = arg
+
+
+if __name__ == "__main__":
+ main()
diff --git a/rp/config/rpki-confgen.xml b/rp/config/rpki-confgen.xml
new file mode 100644
index 00000000..b7bc2f62
--- /dev/null
+++ b/rp/config/rpki-confgen.xml
@@ -0,0 +1,1062 @@
+<!-- -*- SGML -*-
+ $Id$
+
+ Documented option definitions for rpki-confgen to use in generating
+ rpki.conf and TracWiki documentation.
+
+ 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 notice and this permission notice appear in all copies.
+
+ THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS. IN NO EVENT SHALL 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.
+-->
+
+<configuration ident = "$Id$">
+
+ <section name = "myrpki">
+
+ <doc>
+ The "`[myrpki]`" section contains all the parameters that you
+ really need to configure. The name "`myrpki`" is historical and
+ may change in the future.
+ </doc>
+
+ <option name = "handle">
+ <doc>
+ Every resource-holding or server-operating entity needs a
+ "handle", which is just an identifier by which the entity
+ calls itself. Handles do not need to be globally unique, but
+ should be chosen with an eye towards debugging operational
+ problems: it's best if you use a handle that your parents and
+ children will recognize as being you.
+ </doc>
+ <doc>
+ The "`handle`" option in the "`[myrpki]`" section specifies the
+ default handle for this installation. Previous versions of
+ the CA tools required a separate configuration file, each with
+ its own handle setting, for each hosted entity. The current
+ code allows the current handle to be selected at runtime in
+ both the GUI and command line user interface tools, so the
+ handle setting here is just the default when you don't set one
+ explictly. In the long run, this option may go away entirely,
+ but for now you need to set this.
+ </doc>
+ <doc>
+ Syntax is an identifier (ASCII letters, digits, hyphen,
+ underscore -- no whitespace, non-ASCII characters, or other
+ punctuation).
+ </doc>
+ </option>
+
+ <option name = "bpki_servers_directory"
+ value = "${autoconf::datarootdir}/rpki/bpki">
+ <doc>
+ Directory for BPKI files generated by rpkic and used by rpkid
+ and pubd. You will not normally need to change this.
+ </doc>
+ </option>
+
+ <option name = "run_rpkid"
+ value = "yes">
+ <doc>
+ Whether you want to run your own copy of rpkid (and irdbd).
+ Leave this alone unless you're doing something unusual like
+ running a pubd-only installation.
+ </doc>
+ </option>
+
+ <option name = "rpkid_server_host">
+ <doc>
+ DNS hostname for rpkid. In most cases, this must resolve to a
+ publicly-reachable address to be useful, as your RPKI children
+ will need to contact your rpkid at this address.
+ </doc>
+ </option>
+
+ <option name = "rpkid_server_port"
+ value = "4404">
+ <doc>
+ Server port number for rpkid. This can be any legal TCP port
+ number that you're not using for something else.
+ </doc>
+ </option>
+
+ <option name = "irdbd_server_host"
+ value = "localhost">
+ <doc>
+ DNS hostname for irdbd, or "`localhost`". This should be
+ "`localhost`" unless you really know what you are doing.
+ </doc>
+ </option>
+
+ <option name = "irdbd_server_port"
+ value = "4403">
+ <doc>
+ Server port number for irdbd. This can be any legal TCP port
+ number that you're not using for something else.
+ </doc>
+ </option>
+
+ <option name = "run_pubd"
+ value = "yes">
+ <doc>
+ Whether you want to run your own copy of pubd. In general,
+ it's best to use your parent's pubd if your parent allows you
+ to do so, because this will reduce the overall number of
+ publication sites from which relying parties will need to
+ retrieve data. However, not all parents offer publication
+ service, or you may need to run pubd yourself for reliability
+ reasons, or because you're certifying private address space or
+ private Autonomous System Numbers.
+ </doc>
+ <doc>
+ The out of band setup protocol will attempt to negotiate
+ publication service for you with whatever publication service
+ your parent is using, if it can and if you let it.
+ </doc>
+ </option>
+
+ <option name = "pubd_server_host">
+ <doc>
+ DNS hostname for pubd, if you're running it. This must
+ resolve to a publicly reachable address to be useful.
+ </doc>
+ </option>
+
+ <option name = "pubd_server_port"
+ value = "4402">
+ <doc>
+ Server port number for pubd. This can be any legal TCP port
+ number that you're not using for something else.
+ </doc>
+ </option>
+
+ <option name = "pubd_contact_info">
+ <doc>
+ Contact information to include in offers of repository
+ service. This only matters when you're running pubd. This
+ should be a human readable string, perhaps containing an email
+ address or URL.
+ </doc>
+ </option>
+
+ <option name = "publication_base_directory"
+ value = "${autoconf::datarootdir}/rpki/publication">
+ <doc>
+ Root of local directory tree where pubd should write out published
+ data. You need to configure this, and the configuration should
+ match up with the directory where you point rsyncd. Neither pubd
+ nor rsyncd much cares //where// you tell it to put this stuff, the
+ important thing is that the rsync URIs in generated
+ certificates match up with the published objects so that relying
+ parties can find and verify rpkid's published outputs.
+ </doc>
+ </option>
+
+ <option name = "rrdp_publication_base_directory"
+ value = "${autoconf::datarootdir}/rpki/rrdp-publication">
+ <doc>
+ Root of local directory tree where pubd should write out RRDP
+ files. You need to configure this, and the configuration
+ should match up with the directory where you point the web
+ server (usually Apache) that serves the RRDP files. Neither
+ pubd nor Apache much cares //where// you tell it to put this
+ stuff, the important thing is that all the URIs match up so
+ that relying parties can find and verify rpkid's published
+ outputs.
+ </doc>
+ </option>
+
+ <option name = "publication_rsync_module"
+ value = "rpki">
+ <doc>
+ rsyncd module name corresponding to publication_base_directory.
+ This has to match the module you configured into `rsyncd.conf`.
+ Leave this alone unless you have some need to change it.
+ </doc>
+ </option>
+
+ <option name = "publication_rsync_server"
+ value = "${myrpki::pubd_server_host}">
+ <doc>
+ Hostname and optional port number for rsync URIs. In most cases
+ this should just be the same value as pubd_server_host.
+ </doc>
+ </option>
+
+ <option name = "publication_rrdp_base_uri"
+ value = "https://${myrpki::pubd_server_host}/rrdp/">
+ <doc>
+ Base URI for RRDP notification, snapshot, and delta files.
+ In most cases this should be a HTTPS URL for the directory
+ on the publication server where the notify.xml lives.
+ </doc>
+ </option>
+
+ <option name = "publication_rrdp_notification_uri"
+ value = "${myrpki::publication_rrdp_base_uri}notify.xml">
+ <doc>
+ URI for RRDP notification file. You shouldn't need to change this.
+ </doc>
+ </option>
+
+ <option name = "start_rpkid"
+ value = "${myrpki::run_rpkid}">
+ <doc>
+ rpkid startup control. This should usually have the same value as
+ run_rpkid: the only case where you would want to change this is
+ when you are running the back-end code on a different machine from
+ one or more of the daemons, in which case you need finer control
+ over which daemons to start on which machines. In such cases,
+ run_rpkid controls whether the back-end code is doing things to
+ manage rpkid, while start_rpkid controls whether
+ rpki-start-servers attempts to start rpkid on this machine.
+ </doc>
+ </option>
+
+ <option name = "start_irdbd"
+ value = "${myrpki::run_rpkid}">
+ <doc>
+ irdbd startup control. This should usually have the same value as
+ run_rpkid: the only case where you would want to change this is
+ when you are running the back-end code on a different machine from
+ one or more of the daemons, in which case you need finer control
+ over which daemons to start on which machines. In such cases,
+ run_rpkid controls whether the back-end code is doing things to
+ manage rpkid, while start_irdbd controls whether
+ rpki-start-servers attempts to start irdbd on this machine.
+ </doc>
+ </option>
+
+ <option name = "start_pubd"
+ value = "${myrpki::run_pubd}">
+ <doc>
+ pubd startup control. This should usually have the same value as
+ run_pubd: the only case where you would want to change this is
+ when you are running the back-end code on a different machine from
+ one or more of the daemons, in which case you need finer control
+ over which daemons to start on which machines. In such cases,
+ run_pubd controls whether the back-end code is doing things to
+ manage pubd, while start_pubd controls whether
+ rpki-start-servers attempts to start pubd on this machine.
+ </doc>
+ </option>
+
+ <option name = "shared_sql_engine"
+ value = "mysql">
+ <doc>
+ Database engine to use. Default is MySQL, because that's what
+ we've been using for years. Now that all runtime database
+ access is via Django ORM, changing to another engine supported
+ by Django is just a configuration issue.
+ </doc>
+ <doc>
+ Current supported values are "mysql" (the default), "sqlite3",
+ and "postgresql". In theory it should be straightforward to
+ add support for any SQL engine Django supports.
+ </doc>
+ </option>
+
+ <option name = "shared_sql_username"
+ value = "rpki">
+ <doc>
+ If you're comfortable with having all of the databases use the
+ same SQL username, set that value here. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "shared_sql_password">
+ <doc>
+ If you're comfortable with having all of the databases use the
+ same SQL password, set that value here. You should use a
+ locally generated password either here or in the individual
+ settings below. The installation process generates a random
+ value for this option, which satisfies this requirement, so
+ ordinarily you should have no need to change this option.
+ </doc>
+ </option>
+
+ <option name = "rcynic_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for rcynic's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "rcynic_sql_database"
+ value = "rcynic">
+ <doc>
+ SQL database name for rcynic's database. The default setting of
+ this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "rcynic_sql_username"
+ value = "${myrpki::shared_sql_username}">
+ <doc>
+ If you want to use a separate SQL username for rcynic's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "rcynic_sql_password"
+ value = "${myrpki::shared_sql_password}">
+ <doc>
+ If you want to use a separate SQL password for rcynic's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "rpkid_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for rpkid's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "rpkid_sql_database"
+ value = "rpkid">
+ <doc>
+ SQL database name for rpkid's database. The default setting of
+ this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "rpkid_sql_username"
+ value = "${myrpki::shared_sql_username}">
+ <doc>
+ If you want to use a separate SQL username for rpkid's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "rpkid_sql_password"
+ value = "${myrpki::shared_sql_password}">
+ <doc>
+ If you want to use a separate SQL password for rpkid's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "irdbd_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for irdbd's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "irdbd_sql_database"
+ value = "irdbd">
+ <doc>
+ SQL database for irdbd's database. The default setting of this
+ variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "irdbd_sql_username"
+ value = "${myrpki::shared_sql_username}">
+ <doc>
+ If you want to use a separate SQL username for irdbd's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "irdbd_sql_password"
+ value = "${myrpki::shared_sql_password}">
+ <doc>
+ If you want to use a separate SQL password for irdbd's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "pubd_sql_engine"
+ value = "${myrpki::shared_sql_engine}">
+ <doc>
+ SQL engine to use for pubd's database. The default setting
+ of this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "pubd_sql_database"
+ value = "pubd">
+ <doc>
+ SQL database name for pubd's database. The default setting of
+ this variable should be fine.
+ </doc>
+ </option>
+
+ <option name = "pubd_sql_username"
+ value = "${myrpki::shared_sql_username}">
+ <doc>
+ If you want to use a separate SQL username for pubd's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "pubd_sql_password"
+ value = "${myrpki::shared_sql_password}">
+ <doc>
+ If you want to use a separate SQL password for pubd's database,
+ set it here.
+ </doc>
+ </option>
+
+ <option name = "log-destination"
+ value = "file">
+ <doc>
+ Default logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-directory"
+ value = "/var/log/rpki">
+ <doc>
+ Where to write log files when logging to files.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "info">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "3">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "56">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "rcynic">
+
+ <doc>
+ rcynicng, unlike it's predecessor, uses the same `rpki.conf`
+ file as all the other programs in the RPKI toolkit. Start
+ rcynicng with "`-c filename`" to choose a different
+ configuration file. All options are in the "`[rcynic]`"
+ section.
+ </doc>
+
+ <option name = "sql-engine"
+ value = "${myrpki::rcynic_sql_engine}">
+ <doc>
+ SQL engine for rcynic.
+ </doc>
+ </option>
+
+ <option name = "sql-database"
+ value = "${myrpki::rcynic_sql_database}">
+ <doc>
+ SQL database name for rcynic.
+ </doc>
+ </option>
+
+ <option name = "sql-username"
+ value = "${myrpki::rcynic_sql_username}">
+ <doc>
+ SQL user name for rcynic.
+ </doc>
+ </option>
+
+ <option name = "sql-password"
+ value = "${myrpki::rcynic_sql_password}">
+ <doc>
+ SQL password for rcynic.
+ </doc>
+ </option>
+
+ <option name = "log-destination"
+ value = "${myrpki::log-destination}">
+ <doc>
+ Logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-filename"
+ value = "${myrpki::log-directory}/rcynic.log">
+ <doc>
+ Where to write log file when logging to a file.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "${myrpki::log-level}">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "${myrpki::log-time-limit}">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "${myrpki::log-count}">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "rpkid">
+
+ <doc>
+ rpkid's default config file is the system `rpki.conf` file.
+ Start rpkid with "`-c filename`" to choose a different config
+ file. All options are in the "`[rpkid]`" section. BPKI
+ Certificates and keys may be in either DER or PEM format.
+ </doc>
+
+ <option name = "sql-engine"
+ value = "${myrpki::rpkid_sql_engine}">
+ <doc>
+ SQL engine for rpkid.
+ </doc>
+ </option>
+
+ <option name = "sql-database"
+ value = "${myrpki::rpkid_sql_database}">
+ <doc>
+ SQL database name for rpkid.
+ </doc>
+ </option>
+
+ <option name = "sql-username"
+ value = "${myrpki::rpkid_sql_username}">
+ <doc>
+ SQL user name for rpkid.
+ </doc>
+ </option>
+
+ <option name = "sql-password"
+ value = "${myrpki::rpkid_sql_password}">
+ <doc>
+ SQL password for rpkid.
+ </doc>
+ </option>
+
+ <option name = "server-host"
+ value = "${myrpki::rpkid_server_host}">
+ <doc>
+ Host on which rpkid should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "server-port"
+ value = "${myrpki::rpkid_server_port}">
+ <doc>
+ Port on which rpkid should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "irdb-url"
+ value = "http://${myrpki::irdbd_server_host}:${myrpki::irdbd_server_port}/">
+ <doc>
+ HTTP service URL rpkid should use to contact irdbd. If irdbd is
+ running on the same machine as rpkid, this can and probably should
+ be a loopback URL, since nobody but rpkid needs to talk to irdbd.
+ </doc>
+ </option>
+
+ <option name = "bpki-ta"
+ value = "${myrpki::bpki_servers_directory}/ca.cer">
+ <doc>
+ Where rpkid should look for the BPKI trust anchor. All BPKI
+ certificate verification within rpkid traces back to this
+ trust anchor. Don't change this unless you really know what
+ you are doing.
+ </doc>
+ </option>
+
+ <option name = "rpkid-cert"
+ value = "${myrpki::bpki_servers_directory}/rpkid.cer">
+ <doc>
+ Where rpkid should look for its own BPKI EE certificate. Don't
+ change this unless you really know what you are doing.
+ </doc>
+ </option>
+
+ <option name = "rpkid-key"
+ value = "${myrpki::bpki_servers_directory}/rpkid.key">
+ <doc>
+ Where rpkid should look for the private key corresponding to its
+ own BPKI EE certificate. Don't change this unless you really know
+ what you are doing.
+ </doc>
+ </option>
+
+ <option name = "irdb-cert"
+ value = "${myrpki::bpki_servers_directory}/irdbd.cer">
+ <doc>
+ Where rpkid should look for irdbd's BPKI EE certificate.
+ Don't change this unless you really know what you are doing.
+ </doc>
+ </option>
+
+ <option name = "irbe-cert"
+ value = "${myrpki::bpki_servers_directory}/irbe.cer">
+ <doc>
+ Where rpkid should look for the back-end control client's BPKI EE
+ certificate. Don't change this unless you really know what you
+ are doing.
+ </doc>
+ </option>
+
+ <option name = "log-destination"
+ value = "${myrpki::log-destination}">
+ <doc>
+ Logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-filename"
+ value = "${myrpki::log-directory}/rpkid.log">
+ <doc>
+ Where to write log file when logging to a file.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "${myrpki::log-level}">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "${myrpki::log-time-limit}">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "${myrpki::log-count}">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "irdbd">
+
+ <doc>
+ irdbd's default configuration file is the system `rpki.conf`
+ file. Start irdbd with "`-c filename`" to choose a different
+ configuration file. All options are in the "`[irdbd]`" section.
+ </doc>
+
+ <doc>
+ Since irdbd is part of the back-end system, it has direct access to
+ the back-end's SQL database, and thus is able to pull its own BPKI
+ configuration directly from the database, and thus needs a bit less
+ configuration than the other daemons.
+ </doc>
+
+ <option name = "sql-engine"
+ value = "${myrpki::irdbd_sql_engine}">
+ <doc>
+ SQL engine for irdbd.
+ </doc>
+ </option>
+
+ <option name = "sql-database"
+ value = "${myrpki::irdbd_sql_database}">
+ <doc>
+ SQL database name for irdbd.
+ </doc>
+ </option>
+
+ <option name = "sql-username"
+ value = "${myrpki::irdbd_sql_username}">
+ <doc>
+ SQL user name for irdbd.
+ </doc>
+ </option>
+
+ <option name = "sql-password"
+ value = "${myrpki::irdbd_sql_password}">
+ <doc>
+ SQL password for irdbd.
+ </doc>
+ </option>
+
+ <option name = "server-host"
+ value = "${myrpki::irdbd_server_host}">
+ <doc>
+ Host on which irdbd should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "server-port"
+ value = "${myrpki::irdbd_server_port}">
+ <doc>
+ Port on which irdbd should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "startup-message">
+ <doc>
+ String to log on startup, useful when debugging a collection
+ of irdbd instances at once.
+ </doc>
+ </option>
+
+ <option name = "log-destination"
+ value = "${myrpki::log-destination}">
+ <doc>
+ Logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-filename"
+ value = "${myrpki::log-directory}/irdbd.log">
+ <doc>
+ Where to write log file when logging to a file.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "${myrpki::log-level}">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "${myrpki::log-time-limit}">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "${myrpki::log-count}">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "pubd">
+
+ <doc>
+ pubd's default configuration file is the system `rpki.conf`
+ file. Start pubd with "`-c filename`" to choose a different
+ configuration file. All options are in the "`[pubd]`" section.
+ BPKI certificates and keys may be either DER or PEM format.
+ </doc>
+
+ <option name = "sql-engine"
+ value = "${myrpki::pubd_sql_engine}">
+ <doc>
+ SQL engine for pubd.
+ </doc>
+ </option>
+
+ <option name = "sql-database"
+ value = "${myrpki::pubd_sql_database}">
+ <doc>
+ SQL database name for pubd.
+ </doc>
+ </option>
+
+ <option name = "sql-username"
+ value = "${myrpki::pubd_sql_username}">
+ <doc>
+ SQL user name for pubd.
+ </doc>
+ </option>
+
+ <option name = "sql-password"
+ value = "${myrpki::pubd_sql_password}">
+ <doc>
+ SQL password for pubd.
+ </doc>
+ </option>
+
+ <option name = "publication-base"
+ value = "${myrpki::publication_base_directory}">
+ <doc>
+ Root of directory tree where pubd should write out published data.
+ You need to configure this, and the configuration should match up
+ with the directory where you point rsyncd. Neither pubd nor rsyncd
+ much cares -where- you tell them to put this stuff, the important
+ thing is that the rsync URIs in generated certificates match up
+ with the published objects so that relying parties can find and
+ verify rpkid's published outputs.
+ </doc>
+ </option>
+
+ <option name = "rrdp-publication-base"
+ value = "${myrpki::rrdp_publication_base_directory}">
+ <doc>
+ Root of local directory tree where pubd should write out RRDP
+ files. You need to configure this, and the configuration
+ should match up with the directory where you point the web
+ server (usually Apache) that serves the RRDP files. Neither
+ pubd nor Apache much cares //where// you tell it to put this
+ stuff, the important thing is that all the URIs match up so
+ that relying parties can find and verify rpkid's published
+ outputs.
+ </doc>
+ </option>
+
+ <option name = "server-host"
+ value = "${myrpki::pubd_server_host}">
+ <doc>
+ Host on which pubd should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "server-port"
+ value = "${myrpki::pubd_server_port}">
+ <doc>
+ Port on which pubd should listen for HTTP service requests.
+ </doc>
+ </option>
+
+ <option name = "bpki-ta"
+ value = "${myrpki::bpki_servers_directory}/ca.cer">
+ <doc>
+ Where pubd should look for the BPKI trust anchor. All BPKI
+ certificate verification within pubd traces back to this
+ trust anchor. Don't change this unless you really know what
+ you are doing.
+ </doc>
+ </option>
+
+ <option name = "pubd-cert"
+ value = "${myrpki::bpki_servers_directory}/pubd.cer">
+ <doc>
+ Where pubd should look for its own BPKI EE certificate. Don't
+ change this unless you really know what you are doing.
+ </doc>
+ </option>
+
+ <option name = "pubd-key"
+ value = "${myrpki::bpki_servers_directory}/pubd.key">
+ <doc>
+ Where pubd should look for the private key corresponding to its
+ own BPKI EE certificate. Don't change this unless you really know
+ what you are doing.
+ </doc>
+ </option>
+
+ <option name = "pubd-crl"
+ value = "${myrpki::bpki_servers_directory}/ca.crl">
+ <doc>
+ Where pubd should look for the CRL covering its own BPKI EE
+ certificate. Don't change this unless you really know what
+ you are doing.
+ </doc>
+ </option>
+
+ <option name = "irbe-cert"
+ value = "${myrpki::bpki_servers_directory}/irbe.cer">
+ <doc>
+ Where pubd should look for the back-end control client's BPKI EE
+ certificate. Don't change this unless you really know what you
+ are doing.
+ </doc>
+ </option>
+
+ <option name = "rrdp-base-uri"
+ value = "${myrpki::publication_rrdp_base_uri}">
+ <doc>
+ RRDP base URI for naming snapshots and deltas.
+ </doc>
+ </option>
+
+ <option name = "log-destination"
+ value = "${myrpki::log-destination}">
+ <doc>
+ Logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-filename"
+ value = "${myrpki::log-directory}/pubd.log">
+ <doc>
+ Where to write log file when logging to a file.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "${myrpki::log-level}">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "${myrpki::log-time-limit}">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "${myrpki::log-count}">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "rpki-nanny">
+
+ <option name = "log-destination"
+ value = "${myrpki::log-destination}">
+ <doc>
+ Logging mechanism, can be "file", "syslog", "stderr", or "stdout".
+ </doc>
+ </option>
+
+ <option name = "log-filename"
+ value = "${myrpki::log-directory}/rpki-nanny.log">
+ <doc>
+ Where to write log file when logging to a file.
+ </doc>
+ </option>
+
+ <option name = "log-level"
+ value = "${myrpki::log-level}">
+ <doc>
+ Default logging level.
+ </doc>
+ </option>
+
+ <option name = "log-time-limit"
+ value = "${myrpki::log-time-limit}">
+ <doc>
+ Interval between log file rotations, in hours.
+ Set to zero to disable automatic rotations.
+ </doc>
+ </option>
+
+ <option name = "log-count"
+ value = "${myrpki::log-count}">
+ <doc>
+ How many old logs to keep before deleting.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "web_portal">
+
+ <doc>
+ Glue to allow Django to pull user configuration from this file
+ rather than requiring the user to edit settings.py.
+ </doc>
+
+ <!--
+ We used to have SQL settings for the GUI here, but since
+ they're pretty much required to be identical to the ones for
+ irdbd at this point, the duplicate entries were just another
+ chance to misconfigure something, so I removed them. Not yet
+ sure whether this was the right approach. Too much historical
+ baggage in this file.
+ -->
+
+ <option name = "secret-key">
+ <doc>
+ Site-specific secret key for Django.
+ </doc>
+ </option>
+
+ <option name = "allowed-hosts">
+ <doc>
+ Name of virtual host that runs the Django GUI, if this is not
+ the same as the system hostname. Django's security code wants
+ to know the name of the virtual host on which Django is
+ running, and will fail when it thinks it's running on a
+ disallowed host.
+ </doc>
+ <doc>
+ If you get an error like "Invalid HTTP_HOST header (you may
+ need to set ALLOWED_HOSTS)", you will need to set this option.
+ </doc>
+ </option>
+
+ <option name = "download-directory"
+ value = "/var/tmp">
+ <doc>
+ A directory large enough to hold the RouteViews.org routing table dump
+ fetched by the rpkigui-import-routes script.
+ </doc>
+ </option>
+
+ </section>
+
+ <section name = "autoconf">
+
+ <doc>
+ rpki-confgen --autoconf records the current autoconf settings
+ here, so that other options can refer to them. The section name
+ "autoconf" is magic, don't change it.
+ </doc>
+
+ <option name = "bindir">
+ <doc>
+ Usually /usr/bin or /usr/local/bin.
+ </doc>
+ </option>
+
+ <option name = "datarootdir">
+ <doc>
+ Usually /usr/share or /usr/local/share.
+ </doc>
+ </option>
+
+ <option name = "sbindir">
+ <doc>
+ Usually /usr/sbin or /usr/local/sbin.
+ </doc>
+ </option>
+
+ <option name = "sysconfdir">
+ <doc>
+ Usually /etc or /usr/local/etc.
+ </doc>
+ </option>
+
+ </section>
+
+</configuration>
diff --git a/rp/config/rpki-generate-root-certificate b/rp/config/rpki-generate-root-certificate
new file mode 100755
index 00000000..10b8b194
--- /dev/null
+++ b/rp/config/rpki-generate-root-certificate
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+"""
+Generate an RPKI root certificate for rootd. In most cases you should
+not need to do this; see caveats in the manual about running rootd if
+you think you need this. This script does nothing that can't also be
+done with the OpenSSL command line tool, but on some platforms the
+installed copy of openssl doesn't understand the RFC 3779 extensions.
+"""
+
+import os
+import sys
+import pwd
+import time
+import rpki.x509
+import rpki.config
+import rpki.sundial
+import rpki.autoconf
+import rpki.resource_set
+
+os.environ["TZ"] = "UTC"
+time.tzset()
+
+cfg = rpki.config.argparser(section = "rootd", doc = __doc__)
+
+default_certfile = cfg.get("rpki-root-cert-file", "root.cer")
+default_keyfile = cfg.get("rpki-root-key-file", "root.key")
+default_talfile = os.path.splitext(default_certfile)[0] + ".tal"
+
+cfg.argparser.add_argument("-a", "--asns", help = "ASN resources", default = "0-4294967295")
+cfg.argparser.add_argument("-4", "--ipv4", help = "IPv4 resources", default = "0.0.0.0/0")
+cfg.argparser.add_argument("-6", "--ipv6", help = "IPv6 resources", default = "::/0")
+cfg.argparser.add_argument("--certificate", help = "certificate file", default = default_certfile)
+cfg.argparser.add_argument("--key", help = "key file", default = default_keyfile)
+cfg.argparser.add_argument("--tal", help = "TAL file", default = default_talfile)
+
+args = cfg.argparser.parse_args()
+
+resources = rpki.resource_set.resource_bag(
+ asn = args.asns,
+ v4 = args.ipv4,
+ v6 = args.ipv6)
+
+keypair = rpki.x509.RSA.generate(quiet = True)
+
+sia = (cfg.get("rpki_base_uri") + "/",
+ cfg.get("rpki-root-manifest-uri"),
+ None,
+ cfg.get("publication_rrdp_notification_uri", section = "myrpki"))
+
+uris = (cfg.get("rpki-root-cert-uri"),
+ cfg.get("publication_rrdp_base_uri", section = "myrpki") + "root.cer")
+
+cert = rpki.x509.X509.self_certify(
+ keypair = keypair,
+ subject_key = keypair.get_public(),
+ serial = 1,
+ sia = sia,
+ notAfter = rpki.sundial.now() + rpki.sundial.timedelta(days = 365),
+ resources = resources)
+
+with open(args.certificate, "wb") as f:
+ f.write(cert.get_DER())
+
+with open(args.tal, "w") as f:
+ for uri in uris:
+ f.write(uri + "\n")
+ f.write(keypair.get_public().get_Base64())
+
+with os.fdopen(os.open(args.key, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0400), "w") as f:
+ f.write(keypair.get_DER())
+
+try:
+ pw = pwd.getpwnam(rpki.autoconf.RPKI_USER)
+ os.chown(args.key, pw.pw_uid, pw.pw_gid)
+except:
+ pass
diff --git a/rp/config/rpki-manage b/rp/config/rpki-manage
new file mode 100755
index 00000000..ac3cc967
--- /dev/null
+++ b/rp/config/rpki-manage
@@ -0,0 +1,46 @@
+#!/usr/bin/env python
+
+# Using a Python script to run sudo to run a Python script is a bit
+# silly, but it lets us use rpki.autoconf to locate sudo, lets us
+# avoid needing a custom setuid wrapper, lets us avoid another pass
+# through the adventures of shell quoting and tokenization, and
+# generally is just a lot simpler to implement correctly.
+#
+# OK, it's probably a few milliseconds slower. Big deal.
+
+if __name__ == "__main__":
+
+ import os
+ import pwd
+ import sys
+ import rpki.autoconf
+
+ try:
+ uid = pwd.getpwnam(rpki.autoconf.RPKI_USER).pw_uid
+ except:
+ uid = None
+
+ if uid is None or uid == os.geteuid():
+
+ # django-admin seems to have problems creating the superuser account when
+ # $LANG is unset or is set to something totally incompatible with UTF-8.
+
+ if os.environ.get("LANG") in (None, "", "C"):
+ os.environ["LANG"] = "en_US.UTF-8"
+
+ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "rpki.django_settings.gui")
+
+ from django.core.management import execute_from_command_line
+
+ execute_from_command_line()
+
+ else:
+
+ try:
+ argv = [rpki.autoconf.SUDO, "-u", rpki.autoconf.RPKI_USER, sys.executable]
+ argv.extend(os.path.abspath(a) if i == 0 else a for i, a in enumerate(sys.argv))
+ os.execv(argv[0], argv)
+ sys.exit("rpki-manage startup failure, no exception so don't know why, sorry")
+
+ except Exception as e:
+ sys.exit("Couldn't exec sudo python rpki-manage: {!s}".format(e))
diff --git a/rp/config/rpki-sql-backup b/rp/config/rpki-sql-backup
new file mode 100755
index 00000000..09e5856e
--- /dev/null
+++ b/rp/config/rpki-sql-backup
@@ -0,0 +1,63 @@
+#!/usr/bin/env python
+
+# $Id$
+#
+# Copyright (C) 2014 Dragon Research Labs ("DRL")
+# Portions copyright (C) 2010-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.
+
+"""
+Back up data from SQL databases, looking at config file to figure out
+which databases and what credentials to use with them, and eliminating
+duplicates in cases where we've configured multiple applications to
+share a single database.
+"""
+
+import os
+import sys
+import time
+import argparse
+import subprocess
+import rpki.config
+
+os.environ["TZ"] = "UTC"
+time.tzset()
+
+cfg = rpki.config.argparser(doc = __doc__, section = "myrpki")
+cfg.argparser.add_argument("-o", "--output", type = argparse.FileType("wb"), default = sys.stdout,
+ help = "destination for SQL dump (default: stdout)")
+cfg.argparser.add_argument("-v", "--verbose", action = "store_true",
+ help = "whistle while you work")
+args = cfg.argparser.parse_args()
+
+templates = dict(mysql = "mysqldump --add-drop-database -u{username} -p{password} -B{database}",
+ sqlite3 = "sqlite3 {database} .dump",
+ postgresql = "sudo -u {username} pg_dump {database}")
+
+cmds = []
+
+for name in ("rpkid", "irdbd", "pubd"):
+ if cfg.getboolean("start_" + name, False):
+ cmd = templates[cfg.get("sql-engine", section = name)]
+ cmd = cmd.format(database = cfg.get("sql-database", section = name),
+ username = cfg.get("sql-username", section = name),
+ password = cfg.get("sql-password", section = name))
+ if cmd not in cmds:
+ cmds.append(cmd)
+
+for cmd in cmds:
+ if args.verbose:
+ sys.stderr.write("[Running \"{}\"]\n".format(cmd))
+ subprocess.check_call(cmd.split(), stdout = args.output)
diff --git a/rp/config/rpki-sql-setup b/rp/config/rpki-sql-setup
new file mode 100755
index 00000000..6fd64588
--- /dev/null
+++ b/rp/config/rpki-sql-setup
@@ -0,0 +1,348 @@
+#!/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.
+
+"""
+Automated setup of SQL stuff used by the RPKI tools. Pulls
+configuration from rpki.conf, prompts for SQL password when needed.
+"""
+
+import os
+import pwd
+import sys
+import getpass
+import textwrap
+import argparse
+import rpki.config
+
+
+class Abstract_Driver(object):
+
+ # Kludge to make classes derived from this into singletons. Net
+ # of a Million Lies says this is Not Pythonic, but it seems to
+ # work, so long as one doesn't attempt to subclass the resulting
+ # driver classes. For our purposes, it will do.
+
+ __instance = None
+
+ def __new__(cls, *args, **kwargs):
+ if cls.__instance is None:
+ cls.__instance = object.__new__(cls, *args, **kwargs)
+ return cls.__instance
+
+ def db_accessible(self, udb):
+ try:
+ self._db_accessible_test(udb)
+ except:
+ return False
+ else:
+ return True
+
+ def fetchone(self):
+ return self._cur.fetchone()
+
+ def fetchall(self):
+ return self._cur.fetchall()
+
+ def close(self):
+ self._cur.close()
+ self._db.close()
+
+ def log(self, msg):
+ if self.args.verbose:
+ sys.stderr.write(msg + "\n")
+
+
+class MySQL_Driver(Abstract_Driver):
+
+ _initialized = False
+
+ def __init__(self, args):
+ try:
+ self.driver
+ except AttributeError:
+ from rpki.mysql_import import MySQLdb
+ self.driver = MySQLdb
+ self.args = args
+
+ def _db_accessible_test(self, udb):
+ self.driver.connect(db = udb.database, user = udb.username, passwd = udb.password).close()
+
+ def db_exists(self, udb):
+ self.execute("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '{0.database}'".format(udb))
+ return bool(self.fetchone()[0])
+
+ def execute(*args):
+ try:
+ self._cur
+ except AttributeError:
+ self.log("MySQL driver initializing root connection")
+ if self.args.mysql_defaults:
+ mysql_cfg = rpki.config.parser(set_filename = self.args.mysql_defaults, section = "client")
+ self._db = self.driver.connect(db = "mysql",
+ user = mysql_cfg.get("user"),
+ passwd = mysql_cfg.get("password"))
+ else:
+ self._db = self.driver.connect(db = "mysql",
+ user = "root",
+ passwd = getpass.getpass("Please enter your MySQL root password: "))
+ self._db.autocommit(True)
+ self._cur = self._db.cursor()
+ self.log("MySQL driver executing {}".format(", ".join(args)))
+ return self._cur.execute(*args)
+
+ def create(self, udb):
+ self.execute("CREATE DATABASE IF NOT EXISTS {0.database}".format(udb))
+ self.fix_grants(udb)
+
+ def drop(self, udb):
+ self.execute("DROP DATABASE IF EXISTS {0.database}".format(udb))
+
+ def script_drop(self, udb):
+ self.args.script_output.write("DROP DATABASE IF EXISTS {};\n".format(udb.database))
+
+ def fix_grants(self, udb):
+ self.execute("GRANT ALL ON {0.database}.* TO {0.username}@localhost IDENTIFIED BY %s".format(udb),
+ (udb.password,))
+
+class SQLite3_Driver(Abstract_Driver):
+
+ def __init__(self, args):
+ try:
+ self.driver
+ except AttributeError:
+ import sqlite3
+ self.driver = sqlite3
+ self.args = args
+
+ def _db_accessible_test(self, udb):
+ self.driver.connect(udb.database).close()
+
+ def db_exists(self, udb):
+ return os.path.exists(udb.database)
+
+ def _grant(self, udb):
+ if udb.username and os.geteuid() == 0:
+ pw = pwd.getpwnam(udb.username)
+ os.chown(udb.database, pw.pw_uid, pw.pw_gid)
+
+ def create(self, udb):
+ self._db_accessible_test(udb.database)
+ self._grant(udb)
+
+ def drop(self, udb):
+ os.unlink(udb.database)
+
+ def script_drop(self, udb):
+ self.args.script_output.write("rm {}\n".format(udb.database))
+
+ def fix_grants(self, udb):
+ self._grant(udb)
+
+
+class PostgreSQL_Driver(Abstract_Driver):
+
+ def __init__(self, args):
+ try:
+ self.driver
+ except AttributeError:
+ import psycopg2
+ self.driver = psycopg2
+ self.args = args
+ if args.postgresql_root_username and (os.getuid() == 0 or os.geteuid() == 0):
+ self._pw = pwd.getpwnam(args.postgresql_root_username)
+ else:
+ self._pw = None
+ self.log("Initialized PostgreSQL driver, pw {!r}".format(self._pw))
+
+ def _seteuid(self, new_uid):
+ old_uid = os.geteuid()
+ if new_uid != old_uid:
+ self.log("PostgreSQL driver changing EUID from {} to {}".format(old_uid, new_uid))
+ os.seteuid(new_uid)
+ return old_uid
+
+ def execute(self, *args):
+ try:
+ self._cur
+ except AttributeError:
+ self.log("PostgreSQL driver opening connection to database {}".format(self.args.postgresql_root_database))
+ if self._pw is not None:
+ euid = self._seteuid(self._pw.pw_uid)
+ try:
+ self._db = self.driver.connect(database = self.args.postgresql_root_database)
+ self._db.autocommit = True
+ self._cur = self._db.cursor()
+ finally:
+ if self._pw is not None:
+ self._seteuid(euid)
+ self.log("PostgreSQL driver executing {}".format(", ".join(args)))
+ return self._cur.execute(*args)
+
+ def _db_accessible_test(self, udb):
+ pw = pwd.getpwnam(udb.username)
+ uid = self._seteuid(pw.pw_uid)
+ try:
+ self.driver.connect(database = udb.database, user = udb.username , password = udb.password).close()
+ finally:
+ self._seteuid(uid)
+
+ def db_exists(self, udb):
+ self.execute("SELECT COUNT(*) FROM pg_database WHERE datname = '{0.database}'".format(udb))
+ return bool(self.fetchone()[0])
+
+ def role_in_use(self, udb):
+ self.execute(textwrap.dedent('''\
+ SELECT COUNT(*) FROM pg_database
+ JOIN pg_roles ON pg_database.datdba = pg_roles.oid
+ WHERE pg_roles.rolname = '{0.username}'
+ '''.format(udb)))
+ return bool(self.fetchone()[0])
+
+ def create(self, udb):
+ if not self.role_in_use(udb):
+ self.execute("CREATE ROLE {0.username} LOGIN PASSWORD '{0.password}'".format(udb))
+ if not self.db_exists(udb):
+ self.execute("CREATE DATABASE {0.database} OWNER {0.username}".format(udb))
+
+ def drop(self, udb):
+ self.execute("DROP DATABASE IF EXISTS {0.database}".format(udb))
+ if not self.role_in_use(udb):
+ self.execute("DROP ROLE IF EXISTS {0.username}".format(udb))
+
+ def script_drop(self, udb):
+ self.args.script_output.write(textwrap.dedent('''\
+ DROP DATABASE IF EXISTS {0.database};
+ DO $$ BEGIN
+ IF NOT EXISTS (SELECT * FROM pg_database JOIN pg_roles
+ ON pg_database.datdba = pg_roles.oid
+ WHERE pg_roles.rolname = '{0.username}')
+ THEN
+ DROP ROLE IF EXISTS {0.username};
+ END IF;
+ END $$;
+ '''.format(udb)))
+
+ def fix_grants(self, udb):
+ self.execute("ALTER DATABASE {0.database} OWNER TO {0.username}".format(udb))
+ self.execute("ALTER ROLE {0.username} WITH PASSWORD '{0.password}".format(udb))
+
+
+class UserDB(object):
+ """
+ Class to wrap access parameters for a particular database.
+ """
+
+ drivers = dict(sqlite3 = SQLite3_Driver,
+ mysql = MySQL_Driver,
+ postgresql = PostgreSQL_Driver)
+
+ def __init__(self, args, name):
+ self.database = cfg.get("sql-database", section = name)
+ self.username = cfg.get("sql-username", section = name)
+ self.password = cfg.get("sql-password", section = name)
+ self.engine = cfg.get("sql-engine", section = name)
+ self.driver = self.drivers[self.engine](args)
+ self.args = args
+
+ def drop(self):
+ if self.args.force or self.driver.db_accessible(self):
+ self.driver.drop(self)
+
+ def create(self):
+ if self.args.force or not self.driver.db_accessible(self):
+ self.driver.create(self)
+
+ def script_drop(self):
+ self.driver.script_drop(self)
+
+ def drop_and_create(self):
+ if self.args.force or self.driver.db_accessible(self):
+ self.driver.drop(self)
+ self.driver.create(self)
+
+ def fix_grants(self):
+ if self.args.force or not self.driver.db_accessible(self):
+ self.driver.fix_grants(self)
+
+
+parser = argparse.ArgumentParser(description = __doc__)
+parser.add_argument("-c", "--config",
+ help = "specify alternate location for rpki.conf")
+parser.add_argument("-d", "--debug", action = "store_true",
+ help = "enable debugging (eg, Python backtraces)")
+parser.add_argument("-v", "--verbose", action = "store_true",
+ help = "whistle while you work")
+parser.add_argument("-f", "--force", action = "store_true",
+ help = "force database create, drop, or grant regardless of current state")
+
+parser.add_argument("--mysql-defaults",
+ help = "specify MySQL root access credentials via a configuration file")
+
+
+parser.add_argument("--postgresql-root-database", default = "postgres",
+ help = "name of PostgreSQL control database")
+parser.add_argument("--postgresql-root-username",
+ help = "username of PostgreSQL control role")
+
+subparsers = parser.add_subparsers(title = "Commands", metavar = "", dest = "dispatch")
+
+subparsers.add_parser("create",
+ help = "create databases and load schemas")
+
+subparsers.add_parser("drop",
+ help = "drop databases")
+
+subparser = subparsers.add_parser("script-drop",
+ help = "show SQL commands to drop databases")
+subparser.add_argument("script_output",
+ nargs = "?", type = argparse.FileType("w"), default = "-",
+ help = "destination for drop script")
+
+subparsers.add_parser("drop-and-create",
+ help = "drop databases then recreate them and load schemas")
+
+subparsers.add_parser("fix-grants",
+ help = "whack database to match configuration file")
+
+args = parser.parse_args()
+
+try:
+
+ cfg = rpki.config.parser(set_filename = args.config, section = "myrpki")
+
+ names = [name for name in ("irdbd", "rpkid", "pubd")
+ if cfg.getboolean("start_" + name, False)]
+ names.append("rcynic")
+
+ # For now, we quietly ignore missing sections rather than throwing an exception.
+ # I could make a case either way for this, but ignoring missing sections is a
+ # lot easier to clean up while debugging the installation scripts.
+
+ for name in names:
+ if cfg.has_section(name):
+ udb = UserDB(args = args, name = name)
+ method = args.dispatch.replace("-", "_")
+ getattr(udb, method)()
+
+except Exception, e:
+ if args.debug:
+ raise
+ else:
+ sys.exit(str(e))