Ejemplo n.º 1
0
	def add_sni_cert(self, hostname, ssl_certfile=None, ssl_keyfile=None, ssl_version=None):
		try:
			result = super(KingPhisherServer, self).add_sni_cert(hostname, ssl_certfile=ssl_certfile, ssl_keyfile=ssl_keyfile, ssl_version=ssl_version)
		except Exception as error:
			letsencrypt.set_sni_hostname(hostname, ssl_certfile, ssl_keyfile, enabled=False)
			raise error
		letsencrypt.set_sni_hostname(hostname, ssl_certfile, ssl_keyfile, enabled=True)
		return result
Ejemplo n.º 2
0
	def remove_sni_cert(self, hostname):
		for sni_cert in self.sni_certs:
			if sni_cert.hostname == hostname:
				break
		else:
			raise ValueError('the specified hostname does not have an sni certificate configuration')
		result = super(KingPhisherServer, self).remove_sni_cert(hostname)
		letsencrypt.set_sni_hostname(hostname, sni_cert.certfile, sni_cert.keyfile, enabled=False)
		return result
Ejemplo n.º 3
0
	def test_get_sni_hostname_config(self):
		hostname = random_string(16)
		sni_config = letsencrypt.get_sni_hostname_config(hostname)
		self.assertIsNone(sni_config)

		certfile = os.path.join(self.tmp_directory, hostname + '.pem')
		keyfile = os.path.join(self.tmp_directory, hostname + '-key.pem')
		letsencrypt.set_sni_hostname(hostname, certfile, keyfile, enabled=False)
		sni_config = letsencrypt.get_sni_hostname_config(hostname)
		self.assertIsNone(sni_config)

		open(certfile, 'wb')
		open(keyfile, 'wb')
		sni_config = letsencrypt.get_sni_hostname_config(hostname)
		os.remove(certfile)
		os.remove(keyfile)
		self.assertIsInstance(sni_config, letsencrypt.SNIHostnameConfiguration)
		self.assertFalse(sni_config.enabled)
		self.assertEqual(sni_config.certfile, certfile)
		self.assertEqual(sni_config.keyfile, keyfile)
Ejemplo n.º 4
0
def server_from_config(config, handler_klass=None, plugin_manager=None):
    """
	Build a server from a provided configuration instance. If *handler_klass* is
	specified, then the object must inherit from the corresponding
	KingPhisherServer base class.

	:param config: Configuration to retrieve settings from.
	:type config: :py:class:`smoke_zephyr.configuration.Configuration`
	:param handler_klass: Alternative handler class to use.
	:type handler_klass: :py:class:`.KingPhisherRequestHandler`
	:param plugin_manager: The server's plugin manager instance.
	:type plugin_manager: :py:class:`~king_phisher.server.plugins.ServerPluginManager`
	:return: A configured server instance.
	:rtype: :py:class:`.KingPhisherServer`
	"""
    handler_klass = (handler_klass or KingPhisherRequestHandler)
    addresses = get_bind_addresses(config)

    if not len(addresses):
        raise errors.KingPhisherError(
            'at least one address to listen on must be specified')

    ssl_certfile = None
    ssl_keyfile = None
    if config.has_option('server.ssl_cert'):
        ssl_certfile = config.get('server.ssl_cert')
        if not os.access(ssl_certfile, os.R_OK):
            logger.critical(
                "setting server.ssl_cert file '{0}' not found".format(
                    ssl_certfile))
            raise errors.KingPhisherError(
                'invalid ssl configuration, missing certificate file')
        logger.info("using default ssl cert file '{0}'".format(ssl_certfile))
        if config.has_option('server.ssl_key'):
            ssl_keyfile = config.get('server.ssl_key')
            if not os.access(ssl_keyfile, os.R_OK):
                logger.critical(
                    "setting server.ssl_key file '{0}' not found".format(
                        ssl_keyfile))
                raise errors.KingPhisherError(
                    'invalid ssl configuration, missing key file')

    if any([address.ssl for address in addresses]):
        ssl_hostnames = get_ssl_hostnames(config)
        if ssl_certfile is None:
            if not ssl_hostnames:
                raise errors.KingPhisherError(
                    'an ssl certificate must be specified when ssl is enabled')
            sni_config = ssl_hostnames[0]
            logger.warning(
                'no default certificate file was specified, using ssl host configuration: '
                + sni_config.hostname)
            ssl_certfile = sni_config.certfile
            ssl_keyfile = sni_config.keyfile
    else:
        ssl_certfile = None
        ssl_keyfile = None
        ssl_hostnames = []

    try:
        server = KingPhisherServer(config,
                                   plugin_manager,
                                   handler_klass,
                                   addresses=addresses,
                                   ssl_certfile=ssl_certfile,
                                   ssl_keyfile=ssl_keyfile)
    except socket.error as error:
        error_number, error_message = error.args
        error_message = "socket error #{0} ({1})".format(
            (error_number or 'NOT-SET'), error_message)
        if error_number == 98:
            logger.error('failed to bind server to address (socket error #98)')
        logger.error(error_message, exc_info=True)
        raise errors.KingPhisherError(error_message) from None
    if config.has_option('server.server_header'):
        server.server_version = config.get('server.server_header')
        logger.info(
            "setting the server version to the custom header: '{0}'".format(
                config.get('server.server_header')))

    for hostname, ssl_certfile, ssl_keyfile in ssl_hostnames:
        sni_config = letsencrypt.get_sni_hostname_config(hostname, config)
        if sni_config is None:
            letsencrypt.set_sni_hostname(hostname,
                                         ssl_certfile,
                                         ssl_keyfile,
                                         enabled=True)

    for hostname, sni_config in letsencrypt.get_sni_hostnames(
            config, check_files=False).items():
        if not sni_config.enabled:
            continue
        if not _server_add_sni_cert(server, hostname, sni_config):
            letsencrypt.set_sni_hostname(hostname,
                                         sni_config.certfile,
                                         sni_config.keyfile,
                                         enabled=False)

    signals.server_initialized.send(server)
    return server
Ejemplo n.º 5
0
def rpc_ssl_letsencrypt_issue(handler, hostname, load=True):
	"""
	Issue a certificate with Let's Encrypt. This operation can fail for a wide
	variety of reasons, check the ``message`` key of the returned dictionary for
	a string description of what occurred. Successful operation requires that
	the certbot utility be installed, and the server's Let's Encrypt data path
	is configured.

	.. versionadded:: 1.14.0

	:param str hostname: The hostname of the certificate to issue.
	:param bool load: Whether or not to load the certificate once it has been issued.
	:return: A dictionary containing the results of the operation.
	:rtype: dict
	"""
	config = handler.config
	result = {'success': False}

	letsencrypt_config = config.get_if_exists('server.letsencrypt', {})
	# step 1: ensure that a letsencrypt configuration is available
	data_path = letsencrypt_config.get('data_path')
	if not data_path:
		result['message'] = 'Let\'s Encrypt is not configured for use.'
		return result
	if not os.path.isdir(data_path):
		rpc_logger.info('creating the letsencrypt data directory')
		os.mkdir(data_path)

	# step 2: ensure that SSL is enabled already
	if not _ssl_is_enabled(handler):
		result['message'] = 'Can not issue certificates when SSL is not in use.'
		return result
	if not advancedhttpserver.g_ssl_has_server_sni:
		result['message'] = 'Can not issue certificates when SNI is not available.'
		return result

	# step 3: ensure that the certbot utility is available
	bin_path = letsencrypt_config.get('certbot_path') or startup.which('certbot')
	if not bin_path:
		result['message'] = 'Can not issue certificates without the certbot utility.'
		return result

	# step 4: ensure the hostname looks legit (TM) and hasn't already been issued
	if re.match(r'^[a-z0-9][a-z0-9-]*(\.[a-z0-9-]+)+$', hostname, flags=re.IGNORECASE) is None:
		result['message'] = 'Can not issue certificates for invalid hostnames.'
		return result
	if letsencrypt.get_sni_hostname_config(hostname, config):
		result['message'] = 'The specified hostname already has the necessary files.'
		return result

	# step 5: determine the web_root path for this hostname and create it if necessary
	web_root = config.get('server.web_root')
	if config.get('server.vhost_directories'):
		web_root = os.path.join(web_root, hostname)
		if not os.path.isdir(web_root):
			rpc_logger.info('vhost directory does not exist for hostname: ' + hostname)
			os.mkdir(web_root)

	# step 6: issue the certificate with certbot, this starts the subprocess and may take a few seconds
	with _lend_semaphore(handler):
		status = letsencrypt.certbot_issue(web_root, hostname, bin_path=bin_path, unified_directory=data_path)
	if status != os.EX_OK:
		result['message'] = 'Failed to issue the certificate.'
		return result

	# step 7: ensure the necessary files were created
	sni_config = letsencrypt.get_sni_hostname_config(hostname, config)
	if sni_config is None:
		result['message'] = 'The certificate files were not generated.'
		return result

	# step 8: store the data in the database so it can be loaded next time the server starts
	if load:
		handler.server.add_sni_cert(hostname, ssl_certfile=sni_config.certfile, ssl_keyfile=sni_config.keyfile)
	else:
		letsencrypt.set_sni_hostname(hostname, sni_config.certfile, sni_config.certfile, enabled=False)

	result['success'] = True
	result['message'] = 'The operation completed successfully.'
	return result