diff --git a/server/init-script/convergence.rhel6 b/server/init-script/convergence.rhel6 new file mode 100755 index 0000000..5fb9650 --- /dev/null +++ b/server/init-script/convergence.rhel6 @@ -0,0 +1,94 @@ +#!/bin/sh +# +# crond Start/Stop the convergence notary +# +# chkconfig: 345 95 10 +# description: convergence notaries provide an alternative to SSL trust chains + +### BEGIN INIT INFO +# Provides: convergence +# Required-Start: $local_fs $network $named $time +# Required-Stop: $local_fs $network $named $time +# Should-Start: $syslog +# Should-Stop: $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts The convergence daemon processes +# Description: Start convergence notary. +### END INIT INFO + +RETVAL=0 +prog="convergence" +exec=/usr/bin/convergence-notary +lockfile=/var/lock/subsys/convergence +config=/etc/sysconfig/convergence +pidfile=/var/run/convergence.pid + +# Source function library. +. /etc/rc.d/init.d/functions + +[ -e ${config} ] && . ${config} + +start() { + if [ $UID -ne 0 ] ; then + echo "User has insufficient privilege." + exit 4 + fi + [ -x $exec ] || exit 5 + [ -f $config ] || exit 6 + echo -n $"Starting $prog: " + $exec -p $PORT -s $SSL -c $CERT -k $KEY -i "$INTERFACE" -u "$USER" -g "$GROUP" && \ + success || failure + retval=$? + echo + [ $retval -eq 0 ] && touch $lockfile + return $retval +} + +stop() { + if [ $UID -ne 0 ] ; then + echo "User has insufficient privilege." + exit 4 + fi + echo -n $"Stopping $prog: " + if [ -n "`pidfileofproc $prog`" ]; then + killproc $exec + RETVAL=0 + else + failure $"Stopping $prog" + fi + retval=$? + echo + [ $retval -eq 0 ] && rm -f $lockfile + return $retval +} + +restart() { + stop + start +} + +rh_status() { + status -p $pidfile $prog +} + + +case "$1" in + start) + $1 + ;; + stop) + $1 + ;; + restart) + $1 + ;; + status) + rh_status + ;; + *) + echo $"Usage: $0 {start|stop|status|restart}" + exit 2 +esac +exit $? + diff --git a/server/init-script/convergence.ubu11 b/server/init-script/convergence.ubu11 new file mode 100755 index 0000000..af4ef55 --- /dev/null +++ b/server/init-script/convergence.ubu11 @@ -0,0 +1,132 @@ +#! /bin/bash + +### BEGIN INIT INFO +# Provides: convergence +# Required-Start: $local_fs $remote_fs $network $named $time +# Required-Stop: $local_fs $remote_fs $network $named $time +# Should-Start: $syslog +# Should-Stop: $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts The convergence daemon processes +# Description: Start convergence notary. +### END INIT INFO + +set -e + +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin +DAEMON=/usr/local/bin/convergence-notary +NAME=convergence +DESC="convergence notary" +CONVERGENCEPID=/var/run/convergence.pid +WAITFORDAEMON=30 +ARGS="" +# Let's try to figure our some sane defaults: +if [ -r /proc/sys/fs/file-max ]; then + system_max=`cat /proc/sys/fs/file-max` + if [ "$system_max" -gt "80000" ] ; then + MAX_FILEDESCRIPTORS=32768 + elif [ "$system_max" -gt "40000" ] ; then + MAX_FILEDESCRIPTORS=16384 + elif [ "$system_max" -gt "10000" ] ; then + MAX_FILEDESCRIPTORS=8192 + else + MAX_FILEDESCRIPTORS=1024 + cat << EOF + +Warning: Your system has very few filedescriptors available in total. + +Maybe you should try raising that by adding 'fs.file-max=100000' to your +/etc/sysctl.conf file. Feel free to pick any number that you deem appropriate. +Then run 'sysctl -p'. See /proc/sys/fs/file-max for the current value, and +file-nr in the same directory for how many of those are used at the moment. + +EOF + fi +else + MAX_FILEDESCRIPTORS=8192 +fi + +NICE="" + +test -x $DAEMON || exit 0 + +test -f /etc/default/$NAME && . /etc/default/$NAME + +wait_for_deaddaemon () { + pid=$1 + sleep 1 + if test -n "$pid" + then + if kill -0 $pid 2>/dev/null + then + echo -n "." + cnt=0 + while kill -0 $pid 2>/dev/null + do + cnt=`expr $cnt + 1` + if [ $cnt -gt $WAITFORDAEMON ] + then + echo " FAILED." + return 1 + fi + sleep 1 + echo -n "." + done + fi + fi + return 0 +} + + +case "$1" in + start) + + if [ -n "$MAX_FILEDESCRIPTORS" ]; then + echo -n "Raising maximum number of filedescriptors (ulimit -n) to $MAX_FILEDESCRIPTORS" + if ulimit -n "$MAX_FILEDESCRIPTORS" ; then + echo "." + else + echo ": FAILED." + fi + fi + + echo "Starting $DESC: $NAME..." + + start-stop-daemon --start --quiet --oknodo \ + --pidfile $CONVERGENCEPID \ + $NICE \ + --exec $DAEMON -- $ARGS + echo "done." + ;; + stop) + echo -n "Stopping $DESC: " + pid=`cat $CONVERGENCEPID 2>/dev/null` || true + + if test ! -f $CONVERGENCEPID -o -z "$pid"; then + echo "not running (there is no $CONVERGENCEPID)." + exit 0 + fi + + if start-stop-daemon --stop --signal INT --quiet --pidfile $CONVERGENCEPID; then + wait_for_deaddaemon $pid + echo "$NAME." + elif kill -0 $pid 2>/dev/null + then + echo "FAILED (Is $pid not $NAME? Is $DAEMON a different binary now?)." + else + echo "FAILED ($DAEMON died: process $pid not running; or permission denied)." + fi + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload}" >&2 + exit 1 + ;; +esac + +exit 0 diff --git a/server/installer/README.txt b/server/installer/README.txt new file mode 100644 index 0000000..49843c1 --- /dev/null +++ b/server/installer/README.txt @@ -0,0 +1,12 @@ + +Convergence Installer +--------------------- + +See the COPYING above for license restrictions. + +The convergence-installer.py does a complete install of all +convergence components. + +See: convgergence-installer.py -h + +for more information. diff --git a/server/installer/convergence-installer.py b/server/installer/convergence-installer.py new file mode 100755 index 0000000..005e01d --- /dev/null +++ b/server/installer/convergence-installer.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python +"""convergence-install.py installs the Convergence Notary System.""" + +__author__ = "Hugo Connery" +__email__ = "hmco@env.dtu.dk" +__credit__ = "Moxie Marlinspike" +__license__= """ +Copyright (c) 2011 Hugo Connery + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 3 of the +License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 +USA + +""" + +import sys,os + +if sys.version_info < (2, 6): + print "Sorry, convergence requires at least Python 2.6" + sys.exit(1) + +import convergence_installer +import sys, string, os, getopt, pwd + +version = "0.1" +ME = os.path.basename(sys.argv[0]) +CONV_DIR = os.path.join(os.path.dirname(sys.argv[0]), '..') + +os.environ['PATH'] = os.environ['PATH'] + os.pathsep.join(['/usr/bin/', '/usr/local/bin']) + +def parseOptions(argv): + # ignore for now: needs to be written into the service config + uname = 'nobody' + gname = 'nogroup' + # core data for install + sslPort = 443 + httpPort = 80 + incomingInterface = '' + siteName = '' + osType = '' + orgName = '' + bundleUrl = '' + auto_create = 0 + + try: + opts, args = getopt.getopt(argv, "N:b:p:s:i:u:g:n:o:ha") + + for opt, arg in opts: + if opt in("-n"): + siteName = arg + if opt in("-N"): + orgName = arg + if opt in("-b"): + bundleUrl = arg + if opt in("-o"): + osType = arg + if opt in("-p"): + httpPort = int(arg) + elif opt in ("-s"): + sslPort = int(arg) + elif opt in ("-i"): + incomingInterface = arg # IP address + elif opt in ("-u"): + uname = arg # user name + elif opt in ("-g"): + gname = arg # group name + elif opt in ("-a"): + auto_create = 1 # auto-create user/group + elif opt in ("-h"): + usage() + sys.exit(0) + + if ( '' == siteName or '' == osType or '' == orgName ): + print "siteName, orgName and osType are mandatory args" + print "for help use: " + ME + " -h" + sys.exit(2) + if ( '' == bundleUrl ): + bundleUrl = 'https://' + siteName + '/' + siteName + '.notary' + config = convergence_installer.Config( + ME, CONV_DIR, httpPort, sslPort, + uname, gname, incomingInterface, + siteName, osType, orgName, bundleUrl, + auto_create) + if ( not config.verify() ): + sys.exit("Config looks bad") + return config + + except getopt.GetoptError: + usage() + sys.exit(2) + +def usage(): + # Bit of a kludge to get the supported os's + os = convergence_installer.OS(ME, '') + supported = os.get_supported_os() + s = ["convergence-installer.py " + str(version) + "\n", + "Usage: " + ME + " \n", + "The convergence-installer.py attempts to help you get a notary", + "running quickly. Software is installed, a certificate, key ", + "and bundle are created, and a service is create, lauched and ", + "configured for later auto-launch (amongst other things).\n", + "A report is produced after a successful install telling you ", + "where things are and anything else that needs be done.\n", + "Please read the Options carefully and choose what you need.\n", + "Options:\n", + "Mandatory:\n", + "\t-n The DNS name of the host to run the service", + "\t-N The name of the org or person who hosts the service", + "\t\t\t(Informational, but required)", + "\t-o The type of OS into which to install", + "\t\t\t(See 'Supported os types' below)", + "\nOptional: (with [default])\n", + "\t-b The URL at which the bundle file will be published", + "\t\t\t[https:///.notary]", + "\t-p HTTP port to listen on [80]", + "\t-s SSL port to listen on [443]", + "\t-i
IP address to listen on for incoming connections [all]", + "\t-u Name of user to drop privileges to [nobody]", + "\t-g Name of group to drop privileges to [nogroup]", + "\t-a Auto-create user and/or group if they dont exist [no]", + "\nor", + "\t-h Print this help message.\n", + "Suppored os types are:\n\n\t" + supported + "\n", + "More information is available at: https://github.com/moxie0/Convergence/wiki\n" ] + print "\n".join(s) + return 1 + +def make_os_installer(config): + retval = 0; + # Create the real OS installer + if ( config.os_install == 'rhel6' ): + retval = convergence_installer.RHEL6(ME, config) + elif ( config.os_install == 'ubu11' ): + retval = convergence_installer.UBU11(ME, config) + else: + # UNREACHED: should never happen as the config verifies the OS + sys.exit('Unsupported OS') + return retval + +def report(core, os): + service_defn = os.service_init_path + service_data = os.service_data_dir + service_config = os.service_config_file + conv_db = core.conv_db_path + bundle = os.bundle_path_final() + key = os.key_path() + print "\nINSTALL REPORT\n=============\n" + print "\nInstallation is complete, and the service is actually running" + print "\nService:\n" + print "* init script = " + service_defn + print "* data (cert, key and bundle) in " + service_data + print "* config = " + service_config + print "* database = " + conv_db + "\n" + print "You still need to:\n" + print "* copy the notary file to the publish location:" + print " E.g cp " + bundle + " /some/web-site/location/" + print "* check the security on the service data location which has the key and cert" + print " E.g chown -R root:root " + service_data + " ; chmod -R 644 " + service_data + " ;\n chmod 400 " + key + +# Use the core and OS (child) objects to do all the things that need be done +def main(argv): + config = parseOptions(argv) + core = convergence_installer.Core(ME, config) + os_inst = make_os_installer(config) + retval = 1 + if ( config.auto_create ): + retval = os_inst.auto_create_user_group() + if ( retval ): + retval = core.make_staging_dir() and \ + core.install_convergence_software() and \ + core.gen_cert() and \ + os_inst.depend_install() and \ + core.createdb() and \ + os_inst.create_bundle() and \ + os_inst.make_service() and \ + os_inst.install_service_data() and \ + os_inst.make_service_config() and \ + os_inst.service_start() and \ + os_inst.service_auto_start() + if ( retval ): + report(core, os_inst) + return retval + +if __name__ == '__main__': + sys.exit(not main(sys.argv[1:])) diff --git a/server/installer/convergence_installer/__init__.py b/server/installer/convergence_installer/__init__.py new file mode 100644 index 0000000..83eb537 --- /dev/null +++ b/server/installer/convergence_installer/__init__.py @@ -0,0 +1,498 @@ + +import os, sys, shutil, json, grp, pwd + +# the name of the product +conv_name = 'Convergence' +# the name of the service +conv_svc = 'convergence' + +# Should not need to know this (we're creating bundles and shouldn't be) +# notary 'bundle' file version number +bundle_version = '1' + +# Simple messaging tool, and basic file operations (super-class of all) +class Base: + def __init__(self, ME): + self.me = ME + + def msg(self, s): + print >> sys.stderr, s; + + def err(self, s): + self.msg(': '.join([self.me, 'Error', s])) + + def info(self, s): + self.msg(': '.join([self.me, 'Info', s])) + + def ok(self, s): + self.msg(': '.join([self.me, 'SUCCESS', s])) + + def fail(self, s): + self.msg(': '.join([self.me, 'FAILURE', s])) + + def report(self, status, msg): + if ( status ): + self.ok(msg) + else: + self.fail(msg) + return status + + def run_and_report(self, cmd, msg): + return self.report( ( 0 == os.system(cmd) ), msg) + + # convenience function; make a dir + def make_dir(self, d): + retval = 1 + if ( not os.path.isdir(d) ): + os.mkdir(d) + if ( not os.path.isdir(d) ): + self.err("Cant make directory: " + d); + retval = 0 + return retval + + # convenience function; copy a file + def copy_file(self, src_dir, dst_dir, f): + src_path = os.path.join(src_dir, f) + if ( os.path.isdir(src_dir) and os.path.isdir(dst_dir)): + shutil.copy2(src_path, dst_dir) + retval = os.path.isfile(os.path.join(dst_dir, f)) + if ( not retval ): + dst_path = os.path.join(dst_dir, f) + self.err("Failure copying " + src_path + ' to ' + dst_path) + return retval + +# The config supplied to the installer for verification and access +# i.e all the things that we need to know to perform an install +# +# also supplies some simple functions needed both by Core and OS +# (as they both have a Config object) +class Config(Base): + def __init__(self, ME, unpack_dir, http, ssl, uname, gname, + interf, name, os, org_name, bundle_url, auto_create): + Base.__init__(self, ME) + # Not config, but needed in various places + self.unpack_dir = unpack_dir + # the config data + self.org_name = org_name + self.bundle_url = bundle_url + self.http = http + self.ssl = ssl + self.auto_create = auto_create + self.uname = uname + self.uname_exists = 0 + self.gname = gname + self.gname_exists = 0 + self.interf = interf + self.name = name + # The 'string' describing the os by the user + self.os = os + # the actual 'string' for the os that is need for the install + # see OS.translate_supported_os() + self.os_install = '' + + # helper routines + def key_file(self): + return self.name + '.key' + + def cert_file(self): + return self.name + '.pem' + + def bundle_file(self): + return self.name + '.notary' + + # We use a staging dir to create the stuff we need and then later + # move it to its destination. The staging dir is under the + # unpack_dir and has the name of the DNS name of the convergence site + def staging(self): + return os.path.join(self.unpack_dir, self.name) + + # Convenvience function: cd to 'directory' and do something + def cd_dir_and(self, directory, cmd): + return '( cd ' + directory + ' && ' + cmd + ' )' + + # Convenvience function: cd to staging and do something + def cd_staging_and(self, cmd): + return self.cd_dir_and(self.staging(), cmd) + + # + # Verify config entries + # + def is_port(self, port): + return ( 0 < port and port < 65536 ) + + def looks_like_IPv4(self, addr): + split = addr.split('.') + return ( 4 == len(split) ) + + def looks_like_DNS(self, domain): + split = domain.split('.') + return ( 1 < len(split) ) + + def group_exists(self, uname): + retval = 1 + try: + grp.getgrnam(self.gname) + except KeyError: + retval = 0 + return retval + + def user_exists(self, uname): + retval = 1 + try: + pwd.getpwnam(self.uname) + except KeyError: + retval = 0 + return retval + + # check those elements of the config as best we can + def verify(self): + # Check port numbers + msg_port = " is not a valid port" + retval = 1 + if ( not self.is_port(self.http) ): + self.err("httpPort value " + self.http + msg_port) + retval = 0 + if ( not self.is_port(self.ssl) ): + self.err("sslPort value " + self.ssl + msg_port) + retval = 0 + # Check about user and group + msg_not_exist = "' does not exist." + help_auto = "Use -a to auto create missing user/group(s)" + if ( not self.user_exists(self.uname) ): + self.err("User '" + self.uname + msg_not_exist) + if ( not self.auto_create ): + self.info(help_auto) + else: + self.uname_exists = 1 + if ( not self.group_exists(self.gname) ): + self.err("Group '" + self.gname + msg_not_exist) + if ( not self.auto_create ): + self.info(help_auto) + else: + self.gname_exists = 1 + # Incoming interface + if ( 0 < len(self.interf) and not self.looks_like_IPv4(self.interf) ): + self.err("incomingInterface does not look like IPv4") + retval = 0 + if ( not self.looks_like_DNS(self.name) ): + self.err("site name does not look like a DNS domain") + retval = 0 + # requested os is known ?? + os = OS(self.me, self) + self.os_install = os.translate_supported_os(self.os) + retval = ( self.os_install is not None ) + # the bundle_url should be checked in some way + return retval + +# The class that knows about Convergence tools and how to use them for install +class Core(Base): + + def __init__(self, ME, config): + Base.__init__(self, ME) + self.core_gencert = conv_svc + '-gencert' + self.core_bundle = conv_svc + '-bundle' + self.core_createdb = conv_svc + '-createdb' + self.core_notary = conv_svc + '-notary' + # install script + self.install_convergence = 'python ./setup.py install' + # database path (*nix type, unknown elsewhere) + self.conv_db_path = '/var/lib/convergence/convergence.db' + # Our copy of the config + self.config = config + + # We stuff data into the staging area during the install. Create it. + def make_staging_dir(self): + msg = "Create staging dir" + retval = self.make_dir(self.config.staging()) + self.report(retval, msg) + return retval + + # May be too verbose. + def cert_warning(self): + print "\nYou have specified the name\n\n\t" + self.config.name + "\n" + print "as the DNS name for your notary site. \n" + print "Now we generate the Certificate for the site:" + print "You'll need to enter >> THAT NAME AGAIN<< !!\n" + print "I.e the correct answer to the 6'th question looks like\n" + print "Common Name (eg, your name or your server's hostname) []: " + self.config.name + "\n" + + # Generate the certs using the convergence-gencert tool + def gen_cert(self): + # self.cert_warning() + msg = "Generating certificate and key files" + cmd_args = ' -c ' + self.config.cert_file() + \ + ' -k ' + self.config.key_file() + cmd = self.config.cd_staging_and(self.core_gencert + cmd_args) + return self.config.run_and_report(cmd, msg) + + # Install the base software + def install_convergence_software(self): + msg = "Installing " + conv_name + " software" + cmd = self.config.cd_dir_and( + self.config.unpack_dir, self.install_convergence + ) + return self.config.run_and_report(cmd, msg) + + # Create the convergence DB + def createdb(self): + msg = conv_name + " DB shall exist" + exists = os.path.isfile(self.conv_db_path) + retval = 1 + if ( exists ): + self.info(conv_name + " DB already exists, leaving") + else: + retval = self.config.run_and_report(self.core_createdb, msg) + return retval + +# Parent for all OS variants. Provides services for the OS specific +# components of the installer (e.g where to put things). To be sub-classed +# for each OS variant. +class OS(Base): + + def __init__(self, ME, config): + Base.__init__(self, ME) + # to be overridden/defined by child classes, as required + # lots of NON-PORTABLE + # + # The place where we put the cert, key and bundle + self.data_dir = '/etc/' + conv_svc + # The path to the init script (e.g /etc/rc.d/init.d/convergence) + self.service_init_path = '' + # Were in the unpack location is the source file init script + self.service_defn_src_file = '' + # Where do we write a loadable config for the init script + self.service_config_file = '' + # What command will make the service auto start + self.service_auto_start_cmd = '' + # What packages are required + self.depend_packages = [] + # How do we install the packages + self.depend_install_cmd = '' + # Here we stash the config + self.config = config + + # Map of os type to the single definition of how to do it + # e.g rhel6 and fc14 are the same. + self.supported_os = { + 'rhel6': 'rhel6', + 'fc14': 'rhel6', + 'ubu10': 'ubu11', + 'ubu11': 'ubu11', + } + + # where is the final location of the cert + def cert_path(self): + return os.path.join(self.data_dir, self.config.cert_file()) + + # where is the final location of the key + def key_path(self): + return os.path.join(self.data_dir, self.config.key_file()) + + # Where is the bundle after successful install + def bundle_path_final(self): + return os.path.join(self.service_data_dir, self.config.bundle_file()) + + # Return a list of supported OS's + def get_supported_os(self): + return ','.join(self.supported_os.keys()) + + # translate a supported OS to its installer type + # i.e some are the same: e.g rhel6 and fc14 are both 'rhel6' for install + def translate_supported_os(self, os): + retval = None + try: + retval = self.supported_os[os] + except KeyError: + self.err("OS type '" + os + "' is unsupported") + return retval + + # please install my software dependencies + def depend_install(self): + msg = "Installing " + conv_name + " dependencies" + cmd = self.depend_install_cmd + ' ' + ' '.join(self.depend_packages) + return self.config.run_and_report(cmd, msg) + + # To be overridden by a child class + def create_nonpriv_user_cmd(self, uname, gname): + return 'echo "Dont know how to create users" && test 1 -eq 0' + + # To be overridden by a child class + def create_group_cmd(self, gname): + return 'echo "Dont know how to create groups" && test 1 -eq 0' + + def create_nonpriv_user(self, uname, gname): + msg = "Create non-priviledged user '" + uname + "'" + cmd = self.create_nonpriv_user_cmd(uname, gname) + return self.run_and_report(cmd, msg) + + # Same as above. Abstract better + def create_group(self, gname): + msg = "Create group '" + gname + "'" + cmd = self.create_group_cmd(gname) + return self.run_and_report(cmd, msg) + + # auto-create any missing user/group + # + # This requires that the create_{group,user} is implemented for + # all child classes (!!!!!!) + def auto_create_user_group(self): + msg = "Auto-creating User and/or Group" + retval = 1 + if ( not self.config.gname_exists ): + retval = self.create_group(self.config.gname) + if ( not self.config.uname_exists and retval ): + retval = self.create_nonpriv_user( + self.config.uname, self.config.gname + ) + self.report(retval, msg) + return retval + + # Use the OS specific service definition, and create the init file + def make_service(self): + msg = conv_name + " service launch script exists" + src = self.service_init_src + dst = self.service_init_path + retval = 0 + try: + shutil.copy2(src, dst) + retval = 1 + except IOError: + self.err("Failure copying " + src + ' to ' + dst) + self.report(retval, msg) + return retval + + # after building all the useful data in the staging area + # we move it to a system directory from which its data can + # be used by the service itself. + def install_service_data(self): + msg = "Key, cert and bundle installed for service" + dst = self.service_data_dir + if ( self.make_dir(dst) ): + # dirs + src = self.config.staging() + dst = self.service_data_dir + # files + key = self.config.key_file() + cert = self.config.cert_file() + bundle = self.config.bundle_file() + for f in [ key, cert, bundle ]: + self.copy_file(src, dst, f) + retval = ( os.path.isfile(os.path.join(dst,key)) and os.path.isfile(os.path.join(dst,cert)) and os.path.isfile(os.path.join(dst,bundle)) ) + self.report(retval, msg) + return retval + + # Start the service using the generated service (init script) file + def service_start(self): + msg = "Service is just started" + cmd = self.service_start_cmd() + return self.run_and_report(cmd, msg) + + # Configure service auto start + def service_auto_start(self): + msg = "Service will auto-start" + cmd = self.service_auto_start_cmd + return self.run_and_report(cmd, msg) + + def get_cert(self): + cfg = self.config # shorthand + cert = os.path.join(cfg.staging(), cfg.cert_file()) + f = open(cert, 'r') + data = f.read() + f.close() + return data + + # Create the bundle. Bad plan. Re-implements the bundle creator. + # But no choice as the new version is entirely interactive .... + # Could do an echo blah | convergence-bundle.py, but that will break + # just as easily if things change. Need a fully parameterised bundle + # creation script (or a library call). + def bundle_host(self): + return {"host": self.config.name, "http_port": self.config.http , "ssl_port": self.config.ssl, "certificate": self.get_cert() } + + def make_bundle(self): + return {"version": bundle_version, "hosts": [ self.bundle_host() ], "name": self.config.org_name , "bundle_location": self.config.bundle_url } + + def create_bundle(self): + msg = "Notary bundle exists (in staging dir)" + bundle = self.make_bundle() + dst = os.path.join(self.config.staging(), self.config.bundle_file()) + f = open(dst, 'w') + retval = 0 + f.write(json.dumps(bundle)) + f.close() + retval = os.path.isfile(dst) + self.report(retval, msg) + return retval + +# First OS sub-class: things that should be generally the same for SysV type +# Unixes +class sysv_nix(OS): + + def __init__(self, ME, config): + OS.__init__(self, ME, config) + self.service_data_dir = '/etc/' + conv_svc + self.depend_packages = [ + 'python-twisted-web', + 'python-twisted-names', + 'm2crypto' + ] + + def service_start_cmd(self): + return self.service_init_path + ' start' + + def create_nonpriv_user_cmd(self, uname, gname): + return 'useradd -d /tmp -g ' + gname + ' -M -N -s /sbin/false ' + uname + + def create_group_cmd(self, gname): + return 'groupadd ' + gname + + +# OS specifics for RedHat Enterprise Linux 6 (and recent Fedora Core releases) +class UBU11(sysv_nix): + + def __init__(self, ME, config): + sysv_nix.__init__(self, ME, config) + # override + self.service_init_path = '/etc/init.d/' + conv_svc + self.service_init_src = os.path.join(self.config.unpack_dir, 'init-script', conv_svc + '.ubu11') + self.service_config_file = '/etc/default/' + conv_svc + self.depend_install_cmd = 'apt-get -y install' + self.service_auto_start_cmd = 'update-rc.d ' + conv_svc + ' defaults' + + def make_service_config(self): + msg = "Service configuration exists" + config = self.config # shorthand + dst = self.service_config_file + f = open(dst, "w") + param = ["-p " + str(config.http), "-s " + str(config.ssl), "-c " + self.cert_path(), "-k " + self.key_path(), "-u " + config.uname, "-g " + config.gname] + if ( '' != config.interf ): + param.append('-i ' + config.interf) + f.write('ARGS="' + ' '.join(param) + '"') + f.close() + retval = os.path.isfile(dst) + self.report(retval, msg) + return retval + +# OS specifics for RedHat Enterprise Linux 6 (and recent Fedora Core releases) +class RHEL6(sysv_nix): + + def __init__(self, ME, config): + sysv_nix.__init__(self, ME, config) + self.service_init_path = '/etc/rc.d/init.d/' + conv_svc + self.service_init_src = os.path.join(self.config.unpack_dir, 'init-script', conv_svc + '.rhel6') + self.service_config_file = '/etc/sysconfig/' + conv_svc + self.depend_install_cmd = 'yum -y install' + self.service_auto_start_cmd = 'chkconfig ' + conv_svc + ' on' + + def make_service_config(self): + msg = "Service configuration exists" + config = self.config # shorthand + dst = self.service_config_file + f = open(dst, "w") + param = ["PORT=" + str(config.http), "SSL=" + str(config.ssl), "CERT=" + self.cert_path(), "KEY=" + self.key_path(), "USER=" + config.uname, "GROUP=" + config.gname, "INTERFACE=" + config.interf] + f.write('\n'.join(param)) + f.close() + retval = os.path.isfile(dst) + self.report(retval, msg) + return retval + diff --git a/server/installer/developer/ABOUT.txt b/server/installer/developer/ABOUT.txt new file mode 100644 index 0000000..70767bf --- /dev/null +++ b/server/installer/developer/ABOUT.txt @@ -0,0 +1,50 @@ + +This file provides a brief overview of the installer for developers. + +convergence-installer.py + + is the command-line callable for the user. Heavy paramterisation + of all things required for an install, including the OS type. + +convergence_installer/ + + the library that provides all the tools used by the command-line tool. + + +Classes in convergence_installer: + +Base: + + Provides simple messaging and a few file operation primitives + + Is the base class for all other classes. + +Config(Base): + + Is provided the details from the command-line parser. Holds them. + Command-line args are verified using the verify() method. + +Core(Base): + + Knows about the convergence-.py tools (gencert etc.) and uses + them during the install. + +OS(Base): + + The top level of the OS abstraction. A whole bunch of members, + and a few methods, are defined which need to be overridden by + child classes. + + Also defines all of the routines for doing an install based on the + members and methods which are overridden in the child classes. + +sysv_nix(OS): + + Defines the members that are thought to be common for System V + versions of Linux. E.g install service init script to + /etc/rc.d/init.d + +RHEL6(sysv_nix): + + Specifices of the members and methods for RHEL6 (and recent + Fedora Core). diff --git a/server/installer/developer/NOTES.txt b/server/installer/developer/NOTES.txt new file mode 100644 index 0000000..877342a --- /dev/null +++ b/server/installer/developer/NOTES.txt @@ -0,0 +1,12 @@ + +- we are re-implementing the convergence-bundle.py as it is fully + interactive +- require implementation on at least 2 Linuxes (rhel/fc, ubuntu, + ??) + to be happy with the abstraction + +Questions + +Can one publish the bundle at the convergence site itself? +If not, why not? + + diff --git a/server/installer/developer/PURPOSE.txt b/server/installer/developer/PURPOSE.txt new file mode 100644 index 0000000..d29bdab --- /dev/null +++ b/server/installer/developer/PURPOSE.txt @@ -0,0 +1,30 @@ + +The 'installer' area is for the development of an installer that +will do as much as possible for a person who wishes to run a +Convergence Notary. + +INTENTION +--------- + +The idea is that the would-be Notary 'runner' runs one install +script with all data required provided on the command-line. + +The installer then does the rest, leaving the 'runner' with only +a little to do: publish the bundle, check local security. + +LIMITATIONS +----------- + +The current environment uses two components that require human +interaction: the certificate generator, and the bundle generator. + +The first is understandable. The second can/should be 'auto-generated' +so that the human does not need to enter the same data again. + +STATUS +------ + +This installer is only operational on RedHat Enterprise Linux 6 +(and recent Fedora Core) release(s). It needs to embrace as many +Linux distributions as possible, and Windows OSes (and onwards +to BSD, MacOS etc.). diff --git a/server/installer/distro-version-info.txt b/server/installer/distro-version-info.txt new file mode 100644 index 0000000..7b69c36 --- /dev/null +++ b/server/installer/distro-version-info.txt @@ -0,0 +1,8 @@ +/etc/issue + +Fedora release 14 (Laughlin) +Fedora release 16 (Verne) +CentOS Linux release 6.0 (Final) +Ubuntu 11.10 \n \l + + diff --git a/server/installer/foo.txt b/server/installer/foo.txt new file mode 100644 index 0000000..e69de29 diff --git a/server/installer/testing/README b/server/installer/testing/README new file mode 100644 index 0000000..35c1621 --- /dev/null +++ b/server/installer/testing/README @@ -0,0 +1,5 @@ + + +Simple bash(1) based testing framework. + +Do not use this without understanding what it does. diff --git a/server/installer/testing/test.sh b/server/installer/testing/test.sh new file mode 100644 index 0000000..0dfadc7 --- /dev/null +++ b/server/installer/testing/test.sh @@ -0,0 +1,10 @@ + +base=/home/hmco/work/devel/Convergence/server + +function clear_dst() +{ + rm -rf /etc/convergence + rm -f /var/lib/convergence/convergence.db \ + /etc/sysconfig/convergence \ + /etc/rc.d/init.d/convergence +}