Example #1
0
File: cert.py Project: tiran/pki
    def execute(self, argv):

        try:
            opts, args = getopt.gnu_getopt(argv, 'i:v', [
                'instance=', 'input=',
                'verbose', 'debug', 'help'])

        except getopt.GetoptError as e:
            logger.error(e)
            self.print_help()
            sys.exit(1)

        instance_name = 'pki-tomcat'
        cert_file = None

        for o, a in opts:
            if o in ('-i', '--instance'):
                instance_name = a

            elif o == '--input':
                cert_file = a

            elif o == '--debug':
                logging.getLogger().setLevel(logging.DEBUG)

            elif o in ('-v', '--verbose'):
                logging.getLogger().setLevel(logging.INFO)

            elif o == '--help':
                self.print_help()
                sys.exit()

            else:
                logger.error('option %s not recognized', o)
                self.print_help()
                sys.exit(1)

        if len(args) < 1:
            logger.error('Missing cert ID.')
            self.print_help()
            sys.exit(1)

        cert_id = args[0]

        instance = pki.server.instance.PKIInstance(instance_name)

        if not instance.exists():
            logger.error('Invalid instance %s.', instance_name)
            sys.exit(1)

        # Load the instance. Default: pki-tomcat
        instance.load()

        try:
            # Load the cert into NSS db and update all corresponding subsystem's CS.cfg
            instance.cert_import(cert_id, cert_file)

        except pki.server.PKIServerException as e:
            logger.error(str(e))
            sys.exit(1)
Example #2
0
File: cert.py Project: tiran/pki
    def execute(self, argv):
        logging.getLogger().setLevel(logging.INFO)

        try:
            opts, _ = getopt.gnu_getopt(argv, 'i:p:v', [
                'instance=', 'cert=', 'extra-cert=', 'agent-uid=',
                'ldapi-socket=', 'ldap-url=', 'port=', 'verbose', 'debug', 'help',
            ])

        except getopt.GetoptError as e:
            logger.error(e)
            self.print_help()
            sys.exit(1)

        instance_name = 'pki-tomcat'
        all_certs = True
        fix_certs = []
        extra_certs = []
        agent_uid = None
        ldap_url = None
        use_ldapi = False
        port = '8443'

        for o, a in opts:
            if o in ('-i', '--instance'):
                instance_name = a

            elif o == '--cert':
                all_certs = False
                fix_certs.append(a)

            elif o == '--extra-cert':
                try:
                    int(a)
                except ValueError:
                    logger.error('--extra-cert requires serial number as integer')
                    sys.exit(1)
                all_certs = False
                extra_certs.append(a)

            elif o == '--agent-uid':
                agent_uid = a

            elif o == '--ldapi-socket':
                if ldap_url is not None:
                    logger.error('--ldapi-socket cannot be used with --ldap-url')
                    sys.exit(1)
                use_ldapi = True
                ldap_url = 'ldapi://{}'.format(quote(a, safe=''))

            elif o == '--ldap-url':
                if use_ldapi:
                    logger.error('--ldap-url cannot be used with --ldapi-socket')
                    sys.exit(1)
                ldap_url = a

            elif o in ('-p', '--port'):
                port = a
                try:
                    n = int(port)
                    if n < 1 or n > 65535:
                        raise ValueError
                except ValueError:
                    logger.error('-p, --port requires a valid port number as integer')
                    sys.exit(1)

            elif o == '--debug':
                logging.getLogger().setLevel(logging.DEBUG)

            elif o in ('-v', '--verbose'):
                logging.getLogger().setLevel(logging.INFO)

            elif o == '--help':
                self.print_help()
                sys.exit()

            else:
                logger.error('option %s not recognized', o)
                self.print_help()
                sys.exit(1)

        instance = pki.server.instance.PKIInstance(instance_name)

        if not instance.exists():
            logger.error('Invalid instance %s.', instance_name)
            sys.exit(1)

        if not agent_uid:
            logger.error('Must specify --agent-uid')
            sys.exit(1)

        if agent_uid == "pkidbuser":
            logger.error('\'pkidbuser\' cannot be used.')
            sys.exit(1)

        instance.load()

        # 1. Make a list of certs to fix OR use the list provided through CLI options
        if all_certs:
            # TODO: Identify only certs that are EXPIRED or ALMOST EXPIRED
            for subsystem in instance.get_subsystems():
                # Retrieve the subsystem's system certificate
                certs = subsystem.find_system_certs()

                # Iterate on all subsystem's system certificate to prepend
                # subsystem name to the ID
                for cert in certs:
                    if cert['id'] != 'sslserver' and cert['id'] != 'subsystem':
                        cert['id'] = subsystem.name + '_' + cert['id']

                    # Append only unique certificates to other subsystem certificate list
                    # ca_signing isn't supported yet
                    if cert['id'] in fix_certs or cert['id'] == 'ca_signing':
                        continue

                    fix_certs.append(cert['id'])

        logger.info('Fixing the following system certs: %s', fix_certs)
        logger.info('Renewing the following additional certs: %s', extra_certs)

        # Get the CA subsystem and find out Base DN.
        ca_subsystem = instance.get_subsystem('ca')
        basedn = ca_subsystem.get_db_config()['internaldb.basedn']
        dbuser_dn = 'uid=pkidbuser,ou=people,{}'.format(basedn)
        agent_dn = 'uid={},ou=people,{}'.format(agent_uid, basedn)

        dm_pass = ''
        if not use_ldapi:
            # Prompt for DM password
            dm_pass = getpass.getpass(prompt='Enter Directory Manager password: '******'s up
        logger.info('Stopping the instance to proceed with system cert renewal')
        instance.stop()

        # 3. Find the subsystem and disable Self-tests
        try:
            # Placeholder used to hold subsystems whose selftest have been turned off
            # Note: This is initialized as a set to avoid duplicates
            # Example of duplicates:
            # fix_certs = [ca_ocsp_signing, ca_audit_signing] -> will add 'ca' entry twice
            target_subsys = set()

            if 'sslserver' in fix_certs or 'subsystem' in fix_certs:
                # If the cert is either sslserver/subsystem, disable selftest for all
                # subsystems since all subsystems use these 2 certs.
                target_subsys = set(instance.get_subsystems())

            else:
                for cert_id in fix_certs:
                    # Since we already filtered sslserver/subsystem, we can be quite sure
                    # that this split will definitely be of form: <subsys>_<cert_tag>
                    subsystem_name = cert_id.split('_', 1)[0]
                    subsystem = instance.get_subsystem(subsystem_name)

                    # If the subsystem is wrong, stop the process
                    if not subsystem:
                        logger.error('No %s subsystem in instance %s.',
                                     subsystem_name, instance_name)
                        sys.exit(1)

                    target_subsys.add(subsystem)

            if len(extra_certs) > 0:
                target_subsys.add(ca_subsystem)

            # Generate new password for agent account
            agent_pass = gen_random_password()

            with write_temp_file(agent_pass.encode('utf8')) as agent_pass_file, \
                    write_temp_file(dm_pass.encode('utf8')) as dm_pass_file, \
                    ldap_password_authn(
                        instance, target_subsys, dbuser_dn,
                        ldap_url, use_ldapi, dm_pass_file), \
                    suppress_selftest(target_subsys):

                # Verify LDAP connection and DM password
                cmd = ['ldapsearch'] + \
                    ldap_conn_args(ldap_url, use_ldapi, dm_pass_file) + \
                    ['-s', 'base', '-b', basedn, '1.1']
                try:
                    subprocess.check_output(cmd)
                except subprocess.CalledProcessError:
                    logger.error("Failed to connect/authenticate to LDAP at '%s'", ldap_url)
                    sys.exit(1)

                # Reset agent password
                logger.info('Resetting password for %s', agent_dn)
                ldappasswd(ldap_url, use_ldapi, dm_pass_file, agent_dn, agent_pass_file)

                # 4. Bring up the server using a temp SSL cert if the sslcert is expired
                if 'sslserver' in fix_certs:
                    # 4a. Create temp SSL cert
                    logger.info('Creating a temporary sslserver cert')
                    instance.cert_create(cert_id='sslserver', temp_cert=True)

                    # 4b. Delete the existing SSL Cert
                    logger.debug('Removing sslserver cert from instance')
                    instance.cert_del('sslserver')

                    # 4d. Import the temp sslcert into the instance
                    logger.debug('Importing temp sslserver cert')
                    instance.cert_import('sslserver')

                with start_stop(instance):
                    # Place renewal request for all certs in fix_certs
                    for cert_id in fix_certs:
                        logger.info('Requesting new cert for %s', cert_id)
                        instance.cert_create(
                            cert_id=cert_id, renew=True,
                            username=agent_uid, password=agent_pass, secure_port=port)
                    for serial in extra_certs:
                        output = instance.cert_file('{}-renewed'.format(serial))
                        logger.info(
                            'Requesting new cert for %s; writing to %s',
                            serial, output)
                        try:
                            instance.cert_create(
                                serial=serial, renew=True, output=output,
                                username=agent_uid, password=agent_pass, secure_port=port)
                        except pki.PKIException as e:
                            logger.error("Failed to renew certificate %s: %s", serial, e)

                # 8. Delete existing certs and then import the renewed system cert(s)
                for cert_id in fix_certs:
                    # Delete the existing cert from the instance
                    logger.debug('Removing old %s cert from instance %s', cert_id, instance_name)
                    instance.cert_del(cert_id)

                    # Import this new cert into the instance
                    logger.debug('Importing new %s cert into instance %s', cert_id, instance_name)
                    instance.cert_import(cert_id)

                # If subsystem cert was renewed and server was using
                # TLS auth, add the cert to pkidbuser entry
                if dbuser_dn and 'subsystem' in fix_certs:
                    logger.info('Importing new subsystem cert into %s', dbuser_dn)
                    with NamedTemporaryFile(mode='w+b') as der_file:
                        # convert subsystem cert to DER
                        subprocess.check_call([
                            'openssl', 'x509',
                            '-inform', 'PEM', '-outform', 'DER',
                            '-in', instance.cert_file('subsystem'),
                            '-out', der_file.name,
                        ])

                        with write_temp_file(
                            self.PKIDBUSER_LDIF_TEMPLATE
                                .format(dn=dbuser_dn, der_file=der_file.name)
                                .encode('utf-8')
                        ) as ldif_file:
                            # ldapmodify
                            cmd = ['ldapmodify'] + \
                                ldap_conn_args(ldap_url, use_ldapi, dm_pass_file) + \
                                ['-f', ldif_file]
                            subprocess.check_call(cmd)

            # 10. Bring up the server
            logger.info('Starting the instance with renewed certs')
            instance.start()

        except pki.server.PKIServerException as e:
            logger.error(str(e))
            sys.exit(1)