X-Git-Url: http://git.cascardo.info/?p=cascardo%2Fipsilon.git;a=blobdiff_plain;f=ipsilon%2Finstall%2Fipsilon-client-install;h=9959cd0843b89f523a433b364eaa8aa80078659c;hp=f49e351ee2261d1f737266dc609465e33ddb8d40;hb=3fd51fe0d4593cdc39c28f11deafe27845f25584;hpb=a20178b055e783b4146925596e815a05d82b0ac6 diff --git a/ipsilon/install/ipsilon-client-install b/ipsilon/install/ipsilon-client-install index f49e351..9959cd0 100755 --- a/ipsilon/install/ipsilon-client-install +++ b/ipsilon/install/ipsilon-client-install @@ -21,8 +21,9 @@ from ipsilon.tools.saml2metadata import Metadata from ipsilon.tools.saml2metadata import SAML2_NAMEID_MAP from ipsilon.tools.saml2metadata import SAML2_SERVICE_MAP from ipsilon.tools.certs import Certificate -from string import Template +from ipsilon.tools import files import argparse +import ConfigParser import logging import os import pwd @@ -78,13 +79,21 @@ def saml2(): raise path = None - if args['saml_httpd']: + if not args['saml_no_httpd']: path = os.path.join(SAML2_HTTPDIR, args['hostname']) os.makedirs(path, 0750) else: path = os.getcwd() - url = 'https://' + args['hostname'] + proto = 'https' + if not args['saml_secure_setup']: + proto = 'http' + + port_str = '' + if args['port']: + port_str = ':%s' % args['port'] + + url = '%s://%s%s' % (proto, args['hostname'], port_str) url_sp = url + args['saml_sp'] url_logout = url + args['saml_sp_logout'] url_post = url + args['saml_sp_post'] @@ -97,10 +106,11 @@ def saml2(): m.add_certs(c) m.add_service(SAML2_SERVICE_MAP['logout-redirect'], url_logout) m.add_service(SAML2_SERVICE_MAP['response-post'], url_post, index="0") + m.add_allowed_name_format(SAML2_NAMEID_MAP[args['saml_nameid']]) sp_metafile = os.path.join(path, 'metadata.xml') m.output(sp_metafile) - if args['saml_httpd']: + if not args['saml_no_httpd']: idp_metafile = os.path.join(path, 'idp-metadata.xml') with open(idp_metafile, 'w+') as f: f.write(idpmeta) @@ -111,6 +121,7 @@ def saml2(): saml_protect = 'info' saml_auth = '\n' \ ' MellonEnable "auth"\n' \ + ' Header append Cache-Control "no-cache"\n' \ '\n' % args['saml_auth'] psp = '# ' @@ -118,20 +129,35 @@ def saml2(): # default location, enable the default page psp = '' - with open(SAML2_TEMPLATE) as f: - template = f.read() - t = Template(template) - hunk = t.substitute(saml_base=args['saml_base'], - saml_protect=saml_protect, - saml_sp_key=c.key, - saml_sp_cert=c.cert, - saml_sp_meta=sp_metafile, - saml_idp_meta=idp_metafile, - saml_sp=args['saml_sp'], - saml_auth=saml_auth, sp=psp) - - with open(SAML2_CONFFILE, 'w+') as f: - f.write(hunk) + saml_secure = 'Off' + ssl_require = '#' + ssl_rewrite = '#' + if args['port']: + ssl_port = args['port'] + else: + ssl_port = '443' + + if args['saml_secure_setup']: + saml_secure = 'On' + ssl_require = '' + ssl_rewrite = '' + + samlopts = {'saml_base': args['saml_base'], + 'saml_protect': saml_protect, + 'saml_sp_key': c.key, + 'saml_sp_cert': c.cert, + 'saml_sp_meta': sp_metafile, + 'saml_idp_meta': idp_metafile, + 'saml_sp': args['saml_sp'], + 'saml_secure_on': saml_secure, + 'saml_auth': saml_auth, + 'ssl_require': ssl_require, + 'ssl_rewrite': ssl_rewrite, + 'ssl_port': ssl_port, + 'sp_hostname': args['hostname'], + 'sp_port': port_str, + 'sp': psp} + files.write_from_template(SAML2_CONFFILE, SAML2_TEMPLATE, samlopts) files.fix_user_dirs(SAML2_HTTPDIR, args['httpd_user']) @@ -174,6 +200,42 @@ def log_exception(e): logger.error(e) +def parse_config_profile(args): + config = ConfigParser.ConfigParser() + files = config.read(args['config_profile']) + if len(files) == 0: + raise ConfigurationError('Config Profile file %s not found!' % + args['config_profile']) + + if 'globals' in config.sections(): + G = config.options('globals') + for g in G: + val = config.get('globals', g) + if val == 'False': + val = False + elif val == 'True': + val = True + if g in globals(): + globals()[g] = val + else: + for k in globals().keys(): + if k.lower() == g.lower(): + globals()[k] = val + break + + if 'arguments' in config.sections(): + A = config.options('arguments') + for a in A: + val = config.get('arguments', a) + if val == 'False': + val = False + elif val == 'True': + val = True + args[a] = val + + return args + + def parse_args(): global args @@ -184,16 +246,18 @@ def parse_args(): action='version', version='%(prog)s 0.1') parser.add_argument('--hostname', default=socket.getfqdn(), help="Machine's fully qualified host name") + parser.add_argument('--port', default=None, + help="Port number that SP listens on") parser.add_argument('--admin-user', default='admin', help="Account allowed to create a SP") parser.add_argument('--httpd-user', default='apache', help="Web server account used to read certs") - parser.add_argument('--saml', action='store_true', default=False, + parser.add_argument('--saml', action='store_true', default=True, help="Whether to install a saml2 SP") parser.add_argument('--saml-idp-metadata', default=None, help="A URL pointing at the IDP Metadata (FILE or HTTP)") - parser.add_argument('--saml-httpd', action='store_true', default=False, - help="Automatically configure httpd") + parser.add_argument('--saml-no-httpd', action='store_true', default=False, + help="Do not configure httpd") parser.add_argument('--saml-base', default='/', help="Where saml2 authdata is available") parser.add_argument('--saml-auth', default=SAML2_PROTECTED, @@ -204,15 +268,49 @@ def parse_args(): help="Single Logout URL") parser.add_argument('--saml-sp-post', default='/saml2/postResponse', help="Post response URL") + parser.add_argument('--saml-secure-setup', action='store_true', + default=True, help="Turn on all security checks") + parser.add_argument('--saml-nameid', default='unspecified', + choices=SAML2_NAMEID_MAP.keys(), + help="SAML NameID format to use") parser.add_argument('--debug', action='store_true', default=False, help="Turn on script debugging") + parser.add_argument('--config-profile', default=None, + help="File containing install options") parser.add_argument('--uninstall', action='store_true', help="Uninstall the server and all data") args = vars(parser.parse_args()) + if args['config_profile']: + args = parse_config_profile(args) + if len(args['hostname'].split('.')) < 2: - raise ValueError('Hostname: %s is not a FQDN.') + raise ValueError('Hostname: %s is not a FQDN.' % args['hostname']) + + if args['port'] and not args['port'].isdigit(): + raise ValueError('Port number: %s is not an integer.' % args['port']) + + # Validate that all path options begin with '/' + path_args = ['saml_base', 'saml_auth', 'saml_sp', 'saml_sp_logout', + 'saml_sp_post'] + for path_arg in path_args: + if not args[path_arg].startswith('/'): + raise ValueError('--%s must begin with a / character.' % + path_arg.replace('_', '-')) + + # The saml_sp setting must be a subpath of saml_base since it is + # used as the MellonEndpointPath. + if not args['saml_sp'].startswith(args['saml_base']): + raise ValueError('--saml-sp must be a subpath of --saml-base.') + + # The saml_sp_logout and saml_sp_post settings must be subpaths + # of saml_sp (the mellon endpoint). + path_args = ['saml_sp_logout', 'saml_sp_post'] + for path_arg in path_args: + if not args[path_arg].startswith(args['saml_sp']): + raise ValueError('--%s must be a subpath of --saml-sp' % + path_arg.replace('_', '-')) # At least one on this list needs to be specified or we do nothing sp_list = ['saml']