#!/usr/bin/python # # Copyright (C) 2014 Simo Sorce # # see file 'COPYING' for use and warranty information # # 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, see . from ipsilon.login.common import LoginMgrsInstall from ipsilon.providers.common import ProvidersInstall from ipsilon.util.data import Store from ipsilon.tools import files import argparse import cherrypy import logging import os import pwd import shutil import socket import sys import time TEMPLATES = '/usr/share/ipsilon/templates/install' CONFDIR = '/etc/ipsilon' DATADIR = '/var/lib/ipsilon' HTTPDCONFD = '/etc/httpd/conf.d' class ConfigurationError(Exception): def __init__(self, message): super(ConfigurationError, self).__init__(message) self.message = message def __str__(self): return repr(self.message) #Silence cherrypy logging to screen cherrypy.log.screen = False # Regular logging LOGFILE = '/var/log/ipsilon-install.log' logger = logging.getLogger() def openlogs(): global logger # pylint: disable=W0603 if os.path.isfile(LOGFILE): try: created = '%s' % time.ctime(os.path.getctime(LOGFILE)) shutil.move(LOGFILE, '%s.%s' % (LOGFILE, created)) except IOError: pass logger = logging.getLogger() try: lh = logging.FileHandler(LOGFILE) except IOError, e: print >> sys.stderr, 'Unable to open %s (%s)' % (LOGFILE, str(e)) lh = logging.StreamHandler(sys.stderr) formatter = logging.Formatter('[%(asctime)s] %(message)s') lh.setFormatter(formatter) logger.addHandler(lh) def install(plugins, args): logger.info('Installation initiated') now = time.strftime("%Y%m%d%H%M%S", time.gmtime()) instance_conf = os.path.join(CONFDIR, args['instance']) logger.info('Installing default config files') ipsilon_conf = os.path.join(instance_conf, 'ipsilon.conf') idp_conf = os.path.join(instance_conf, 'idp.conf') args['httpd_conf'] = os.path.join(HTTPDCONFD, 'ipsilon-%s.conf' % args['instance']) args['data_dir'] = os.path.join(DATADIR, args['instance']) if os.path.exists(ipsilon_conf): shutil.move(ipsilon_conf, '%s.bakcup.%s' % (ipsilon_conf, now)) if os.path.exists(idp_conf): shutil.move(idp_conf, '%s.backup.%s' % (idp_conf, now)) if not os.path.exists(instance_conf): os.makedirs(instance_conf, 0700) confopts = {'instance': args['instance'], 'datadir': args['data_dir']} files.write_from_template(ipsilon_conf, os.path.join(TEMPLATES, 'ipsilon.conf'), confopts) files.write_from_template(idp_conf, os.path.join(TEMPLATES, 'idp.conf'), confopts) if not os.path.exists(args['httpd_conf']): os.symlink(idp_conf, args['httpd_conf']) os.makedirs(os.path.join(args['data_dir'], 'sessions'), 0700) data_conf = os.path.join(args['data_dir'], 'ipsilon.conf') if not os.path.exists(data_conf): os.symlink(ipsilon_conf, data_conf) # Load the cherrypy config from the newly installed file so # that db paths and all is properly set before configuring # components cherrypy.config.update(ipsilon_conf) # Move pre-existing admin db away admin_db = cherrypy.config['admin.config.db'] if os.path.exists(admin_db): shutil.move(admin_db, '%s.backup.%s' % (admin_db, now)) # Rebuild user db users_db = cherrypy.config['user.prefs.db'] if os.path.exists(users_db): shutil.move(users_db, '%s.backup.%s' % (users_db, now)) db = Store() db.save_user_preferences(args['admin_user'], {'is_admin': 1}) logger.info('Configuring login managers') for plugin_name in args['lm_order']: plugin = plugins['Login Managers'][plugin_name] plugin.configure(args) logger.info('Configuring Authentication Providers') for plugin_name in plugins['Auth Providers']: plugin = plugins['Auth Providers'][plugin_name] plugin.configure(args) # Fixup permissions so only the ipsilon user can read these files files.fix_user_dirs(instance_conf, opts['system_user'], mode=0500) files.fix_user_dirs(args['data_dir'], opts['system_user']) def uninstall(plugins, args): logger.info('Uninstallation initiated') raise Exception('Not Implemented') def find_plugins(): plugins = { 'Login Managers': LoginMgrsInstall().plugins, 'Auth Providers': ProvidersInstall().plugins } return plugins def parse_args(plugins): parser = argparse.ArgumentParser(description='Ipsilon Install Options') parser.add_argument('--version', action='version', version='%(prog)s 0.1') parser.add_argument('-o', '--login-managers-order', dest='lm_order', help='Comma separated list of login managers') parser.add_argument('--hostname', help="Machine's fully qualified host name") parser.add_argument('--instance', default='idp', help="IdP instance name, each is a separate idp") parser.add_argument('--system-user', default='ipsilon', help="User account used to run the server") parser.add_argument('--admin-user', default='admin', help="User account that is assigned admin privileges") parser.add_argument('--ipa', choices=['yes', 'no'], default='yes', help='Detect and use an IPA server for authentication') parser.add_argument('--uninstall', action='store_true', help="Uninstall the server and all data") lms = [] for plugin_group in plugins: group = parser.add_argument_group(plugin_group) for plugin_name in plugins[plugin_group]: plugin = plugins[plugin_group][plugin_name] if plugin.ptype == 'login': lms.append(plugin.name) plugin.install_args(group) args = vars(parser.parse_args()) if not args['hostname']: args['hostname'] = socket.getfqdn() if len(args['hostname'].split('.')) < 2: raise ConfigurationError('Hostname: %s is not a FQDN') try: pwd.getpwnam(args['system_user']) except KeyError: raise ConfigurationError('User: %s not found on the system') if args['lm_order'] is None: args['lm_order'] = [] for name in lms: if args[name] == 'yes': args['lm_order'].append(name) else: args['lm_order'] = args['lm_order'].split(',') if len(args['lm_order']) == 0: #force the basic pam provider if nothing else is selected if 'pam' not in args: parser.print_help() sys.exit(-1) args['lm_order'] = ['pam'] args['pam'] = 'yes' #FIXME: check instance is only alphanums return args if __name__ == '__main__': opts = [] out = 0 openlogs() try: fplugins = find_plugins() opts = parse_args(fplugins) logger.setLevel(logging.DEBUG) logger.info('Intallation arguments:') for k in sorted(opts.iterkeys()): logger.info('%s: %s', k, opts[k]) if 'uninstall' in opts and opts['uninstall'] is True: uninstall(fplugins, opts) install(fplugins, opts) except Exception, e: # pylint: disable=broad-except logger.exception(e) if 'uninstall' in opts and opts['uninstall'] is True: print 'Uninstallation aborted.' else: print 'Installation aborted.' print 'See log file %s for details' % LOGFILE out = 1 finally: if out == 0: if 'uninstall' in opts and opts['uninstall'] is True: print 'Uninstallation complete.' else: print 'Installation complete.' sys.exit(out)