Example #1
0
 def load(self):
     """
     Loads the authentication information from etcd.
     """
     try:
         d = self.ds.get(
             '/commissaire/config/httpbasicauthbyuserlist')
         self._data = json.loads(d.value)
         self.logger.info('Loaded authentication data from Etcd.')
         # TODO: Watch endpoint and reload on changes
     except etcd.EtcdKeyNotFound:
         _, eknf, _ = exception.raise_if_not(etcd.EtcdKeyNotFound)
         self.logger.warn(
             'User configuration not found in Etcd. Raising...')
         self._data = {}
         raise eknf
     except ValueError:
         _, ve, _ = exception.raise_if_not(ValueError)
         self.logger.warn(
             'User configuration in Etcd is not valid JSON. Raising...')
         raise ve
Example #2
0
 def load(self):
     """
     Loads the authentication information from etcd.
     """
     try:
         d = self.ds.get(
             '/commissaire/config/httpbasicauthbyuserlist')
         self._data = json.loads(d.value)
         self.logger.info('Loaded authentication data from Etcd.')
         # TODO: Watch endpoint and reload on changes
     except etcd.EtcdKeyNotFound:
         _, eknf, _ = exception.raise_if_not(etcd.EtcdKeyNotFound)
         self.logger.warn(
             'User configuration not found in Etcd. Raising...')
         self._data = {}
         raise eknf
     except ValueError:
         _, ve, _ = exception.raise_if_not(ValueError)
         self.logger.warn(
             'User configuration in Etcd is not valid JSON. Raising...')
         raise ve
Example #3
0
 def load(self):
     """
     Loads the authentication information from the JSON file.
     """
     try:
         with open(self.filepath, 'r') as afile:
             self._data = json.load(afile)
             self.logger.info('Loaded authentication data from local file.')
     except:
         _, ve, _ = exception.raise_if_not((ValueError, IOError))
         self.logger.warn('Denying all access due to problem parsing '
                          'JSON file: {0}'.format(ve))
         self._data = {}
Example #4
0
    def _load_from_file(self, path):
        """
        Loads authentication information from a JSON file.

        :param path: Path to the JSON file
        :type path: str
        """
        try:
            with open(path, "r") as afile:
                self._data.update(json.load(afile))
                self.logger.info("Loaded authentication data from local file.")
        except:
            _, ve, _ = exception.raise_if_not((ValueError, IOError))
            self.logger.warn("Denying all access due to problem parsing " "JSON file: {0}".format(ve))
Example #5
0
 def load(self):
     """
     Loads the authentication information from the JSON file.
     """
     try:
         with open(self.filepath, 'r') as afile:
             self._data = json.load(afile)
             self.logger.info('Loaded authentication data from local file.')
     except:
         _, ve, _ = exception.raise_if_not((ValueError, IOError))
         self.logger.warn(
             'Denying all access due to problem parsing '
             'JSON file: {0}'.format(ve))
         self._data = {}
    def _load_from_file(self, path):
        """
        Loads authentication information from a JSON file.

        :param path: Path to the JSON file
        :type path: str
        """
        try:
            with open(path, 'r') as afile:
                self._data.update(json.load(afile))
                self.logger.info('Loaded authentication data from local file.')
        except:
            _, ve, _ = exception.raise_if_not((ValueError, IOError))
            self.logger.warn('Denying all access due to problem parsing '
                             'JSON file: {0}'.format(ve))
    def test_raise_if_not(self):
        """
        Verify raise_if_not raises only when it should.
        """
        # Verify raised when not whitelisted
        for x in (ValueError, [ValueError, KeyError]):
            self.assertRaises(TypeError, exception.raise_if_not, x)

        # Verify we get exception information when it is whitelisted
        for x in (ValueError, [ValueError, KeyError]):
            try:
                raise ValueError('test')
            except:
                exc_type, exc, _ = exception.raise_if_not(x)
                self.assertEquals(exc_type, ValueError)
                self.assertTrue(isinstance(exc, ValueError))
                self.assertEquals('test', exc.args[0])
    def test_raise_if_not(self):
        """
        Verify raise_if_not raises only when it should.
        """
        # Verify raised when not whitelisted
        for x in (ValueError, [ValueError, KeyError]):
            self.assertRaises(TypeError, exception.raise_if_not, x)

        # Verify we get exception information when it is whitelisted
        for x in (ValueError, [ValueError, KeyError]):
            try:
                raise ValueError("test")
            except:
                exc_type, exc, _ = exception.raise_if_not(x)
                self.assertEquals(exc_type, ValueError)
                self.assertTrue(isinstance(exc, ValueError))
                self.assertEquals("test", exc.args[0])
Example #9
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    from commissaire.cherrypy_plugins.store import StorePlugin
    from commissaire.cherrypy_plugins.investigator import InvestigatorPlugin
    from commissaire.cherrypy_plugins.watcher import WatcherPlugin

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)

    try:
        args = parse_args(parser)
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    found_logger_config = False
    for logger_path in (
            '/etc/commissaire/logger.json', './conf/logger.json'):
        if os.path.isfile(logger_path):
            with open(logger_path, 'r') as logging_default_cfg:
                logging.config.dictConfig(
                    json.loads(logging_default_cfg.read()))
                found_logger_config = True
            logging.warn('No logger configuration in Etcd. Using defaults '
                         'at {0}'.format(logger_path))
    if not found_logger_config:
        parser.error(
            'Unable to find any logging configuration. Exiting ...')

    cherrypy.server.unsubscribe()
    # Disable autoreloading and use our logger
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': '',
                            'engine.autoreload.on': False})

    new_ssl_adapter_cls = type(
        "CustomClientCertBuiltinSSLAdapter",
        (ClientCertBuiltinSSLAdapter,),
        {"verify_location": args.tls_clientverifyfile}
    )

    if sys.version_info < (3, 0):
        from cherrypy.wsgiserver.wsgiserver2 import ssl_adapters
    else:
        from cherrypy.wsgiserver.wsgiserver3 import ssl_adapters
    ssl_adapters['builtin_client'] = new_ssl_adapter_cls

    server = cherrypy._cpserver.Server()
    server.socket_host = args.listen_interface
    server.socket_port = args.listen_port
    server.thread_pool = 10

    if bool(args.tls_keyfile) ^ bool(args.tls_certfile):
        parser.error(
            'Both a keyfile and certfile must be '
            'given for commissaire server TLS. Exiting ...')
    elif bool(args.tls_clientverifyfile) and not bool(args.tls_certfile):
        parser.error(
            'If a client verify file is given a TLS keyfile and '
            'certfile must be given as well. Exiting ...')

    if args.tls_keyfile and args.tls_certfile:
        server.ssl_module = 'builtin_client'
        server.ssl_certificate = args.tls_certfile
        server.ssl_private_key = args.tls_keyfile
        logging.info('Commissaire server TLS will be enabled.')
    server.subscribe()

    # Handle UNIX signals (SIGTERM, SIGHUP, SIGUSR1)
    if hasattr(cherrypy.engine, 'signal_handler'):
        cherrypy.engine.signal_handler.subscribe()

    # Configure the store plugin before starting it.
    store_plugin = StorePlugin(cherrypy.engine)
    store_manager = store_plugin.get_store_manager()

    # Configure store handlers from user data.
    #
    # FIXME The configuration format got too complicated to easily parse
    #       comma-separated key-value pairs so we punted and switched to
    #       JSON format. The authentication CLI options need reworked to
    #       keep the input formats consistent.
    if len(args.register_store_handler) == 0:
        # Order is significant; Kubernetes must be first.
        args.register_store_handler = [
            C.DEFAULT_KUBERNETES_STORE_HANDLER,
            C.DEFAULT_ETCD_STORE_HANDLER
        ]
    for config in args.register_store_handler:
        if type(config) is str:
            config = json.loads(config)
        if type(config) is dict:
            register_store_handler(parser, store_manager, config)
        else:
            parser.error(
                'Store handler format must be a JSON object, got a '
                '{} instead: {}'.format(type(config).__name__, config))

    # Add our plugins
    InvestigatorPlugin(cherrypy.engine).subscribe()
    WatcherPlugin(cherrypy.engine, store_manager.clone()).subscribe()

    store_plugin.subscribe()

    # NOTE: Anything that requires etcd should start AFTER
    # the engine is started
    cherrypy.engine.start()

    try:
        # Make and mount the app
        authentication_kwargs = {}
        if type(args.authentication_plugin_kwargs) is str:
            if '=' in args.authentication_plugin_kwargs:
                for item in args.authentication_plugin_kwargs.split(','):
                    key, value = item.split('=')
                    authentication_kwargs[key.strip()] = value.strip()
        elif type(args.authentication_plugin_kwargs) is dict:
            # _read_config_file() sets this up.
            authentication_kwargs = args.authentication_plugin_kwargs

        app = create_app(
            args.authentication_plugin,
            authentication_kwargs)
        cherrypy.tree.graft(app, "/")

        # Serve forever
        cherrypy.engine.block()
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        logging.fatal('Unable to start server: {0}'.format(ex))
        cherrypy.engine.stop()
Example #10
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    from commissaire.cherrypy_plugins.store import StorePlugin
    from commissaire.cherrypy_plugins.investigator import InvestigatorPlugin
    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)

    try:
        args = parse_args(parser)
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    store_kwargs = {
        'read_timeout': 2,
        'host': config.etcd['uri'].hostname,
        'port': config.etcd['uri'].port,
        'protocol': config.etcd['uri'].scheme,
    }

    if bool(args.etcd_ca_path):
        config.etcd['certificate_ca_path'] = args.etcd_ca_path

    # We need all args to use a client side cert for etcd
    if bool(args.etcd_cert_path) ^ bool(args.etcd_cert_key_path):
        parser.error(
            'Both etcd-cert-path and etcd-cert-key-path must be '
            'provided to use a client side certificate with etcd.')
    elif bool(args.etcd_cert_path):
        if config.etcd['uri'].scheme != 'https':
            parser.error('An https URI is required when using '
                         'client side certificates.')
        store_kwargs['cert'] = (args.etcd_cert_path, args.etcd_cert_key_path)
        config.etcd['certificate_path'] = args.etcd_cert_path
        config.etcd['certificate_key_path'] = args.etcd_cert_key_path
        logging.info('Using client side certificate for etcd.')

    ds = etcd.Client(**store_kwargs)

    try:
        logging.config.dictConfig(
            json.loads(ds.read('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        found_logger_config = False
        for logger_path in (
                '/etc/commissaire/logger.json', './conf/logger.json'):
            if os.path.isfile(logger_path):
                with open(logger_path, 'r') as logging_default_cfg:
                    logging.config.dictConfig(
                        json.loads(logging_default_cfg.read()))
                    found_logger_config = True
                logging.warn('No logger configuration in Etcd. Using defaults '
                             'at {0}'.format(logger_path))
        if not found_logger_config:
            parser.error(
                'Unable to find any logging configuration. Exiting ...')

    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    # TLS options
    tls_keyfile = cli_etcd_or_default(
        'tlskeyfile', args.tls_keyfile, None, ds)
    tls_certfile = cli_etcd_or_default(
        'tlscertfile', args.tls_certfile, None, ds)
    tls_clientverifyfile = cli_etcd_or_default(
        'tls-clientverifyfile', args.tls_clientverifyfile, None, ds)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)

    # Pull options for accessing kubernetes
    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.info('Using kubetoken for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubetoken set.')
    try:
        config.kubernetes['certificate_path'] = ds.get(
            '/commissaire/config/kube_certificate_path').value
        config.kubernetes['certificate_key_path'] = ds.get(
            '/commissaire/config/kube_certificate_key_path').value
        logging.info('Using client side certificate for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubernetes client side certificate set.')

    # Add our config instance to the cherrypy global config so we can use it's
    # values elsewhere
    # TODO: Technically this should be in the cherrypy.request.app.config
    # but it looks like that isn't accessable with WSGI based apps
    cherrypy.config['commissaire.config'] = config

    logging.debug('Config: {0}'.format(config))

    cherrypy.server.unsubscribe()
    # Disable autoreloading and use our logger
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': '',
                            'engine.autoreload.on': False})

    new_ssl_adapter_cls = type(
        "CustomClientCertBuiltinSSLAdapter",
        (ClientCertBuiltinSSLAdapter,),
        {"verify_location": tls_clientverifyfile}
    )

    if sys.version_info < (3, 0):
        from cherrypy.wsgiserver.wsgiserver2 import ssl_adapters
    else:
        from cherrypy.wsgiserver.wsgiserver3 import ssl_adapters
    ssl_adapters['builtin_client'] = new_ssl_adapter_cls

    server = cherrypy._cpserver.Server()
    server.socket_host = interface
    server.socket_port = int(port)
    server.thread_pool = 10

    if bool(tls_keyfile) ^ bool(tls_certfile):
        parser.error(
            'Both a keyfile and certfile must be '
            'given for commissaire server TLS. Exiting ...')
    elif bool(tls_clientverifyfile) and not bool(tls_certfile):
        parser.error(
            'If a client verify file is given a TLS keyfile and '
            'certfile must be given as well. Exiting ...')

    if tls_keyfile and tls_certfile:
        server.ssl_module = 'builtin_client'
        server.ssl_certificate = tls_certfile
        server.ssl_private_key = tls_keyfile
        logging.info('Commissaire server TLS will be enabled.')
    server.subscribe()

    # Handle UNIX signals (SIGTERM, SIGHUP, SIGUSR1)
    if hasattr(cherrypy.engine, 'signal_handler'):
        cherrypy.engine.signal_handler.subscribe()

    # Add our plugins
    InvestigatorPlugin(cherrypy.engine, config).subscribe()

    # Configure the store plugin before starting it.
    store_plugin = StorePlugin(cherrypy.engine)
    store_manager = store_plugin.get_store_manager()

    # XXX Temporary until we have a real storage plugin system.
    store_manager.register_store_handler(
        EtcdStoreHandler, store_kwargs, BogusModelType)

    store_plugin.subscribe()

    # NOTE: Anything that requires etcd should start AFTER
    # the engine is started
    cherrypy.engine.start()

    try:
        # Make and mount the app
        authentication_kwargs = {}
        if type(args.authentication_plugin_kwargs) is str:
            if '=' in args.authentication_plugin_kwargs:
                for item in args.authentication_plugin_kwargs.split(','):
                    key, value = item.split('=')
                    authentication_kwargs[key.strip()] = value.strip()
        elif type(args.authentication_plugin_kwargs) is dict:
            # _read_config_file() sets this up.
            authentication_kwargs = args.authentication_plugin_kwargs

        app = create_app(
            args.authentication_plugin,
            authentication_kwargs)
        cherrypy.tree.graft(app, "/")

        # Serve forever
        cherrypy.engine.block()
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        logging.fatal('Unable to start server: {0}'.format(ex))
        cherrypy.engine.stop()
Example #11
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    import argparse
    from commissaire.config import Config

    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)
    parser.add_argument(
        '--listen-interface', '-i', type=str, help='Interface to listen on')
    parser.add_argument(
        '--listen-port', '-p', type=int, help='Port to listen on')
    parser.add_argument(
        '--etcd-uri', '-e', type=str, required=True,
        help='Full URI for etcd EX: http://127.0.0.1:2379')
    parser.add_argument(
        '--kube-uri', '-k', type=str, required=True,
        help='Full URI for kubernetes EX: http://127.0.0.1:8080')
    args = parser.parse_args()

    try:
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    ds = etcd.Client(
        host=config.etcd['uri'].hostname, port=config.etcd['uri'].port)

    try:
        logging.config.dictConfig(
            json.loads(ds.get('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        with open('./conf/logger.json', 'r') as logging_default_cfg:
            logging.config.dictConfig(json.loads(logging_default_cfg.read()))
            logging.warn('No logger configuration in Etcd. Using defaults.')
    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)
    config.etcd['listen'] = urlparse('http://{0}:{1}'.format(
        interface, port))

    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.debug('Config: {0}'.format(config))
        POOLS['investigator'].spawn(
            investigator, INVESTIGATE_QUEUE, config, ds)
    except etcd.EtcdKeyNotFound:
        parser.error('"/commissaire/config/kubetoken" must be set in etcd!')
    # watch_thread = gevent.spawn(host_watcher, ROUTER_QUEUE, ds)
    # router_thread = gevent.spawn(router, ROUTER_QUEUE)

    app = create_app(ds)
    try:
        WSGIServer((interface, int(port)), app).serve_forever()
    except KeyboardInterrupt:
        pass

    POOLS['investigator'].kill()
Example #12
0
def main():  # pragma: no cover
    """
    Main script entry point.
    """
    import argparse

    from multiprocessing import Process
    from commissaire.cherrypy_plugins import CherryPyStorePlugin
    config = Config()

    epilog = ('Example: ./commissaire -e http://127.0.0.1:2379'
              ' -k http://127.0.0.1:8080')

    parser = argparse.ArgumentParser(epilog=epilog)
    parser.add_argument(
        '--listen-interface', '-i', type=str, help='Interface to listen on')
    parser.add_argument(
        '--listen-port', '-p', type=int, help='Port to listen on')
    parser.add_argument(
        '--etcd-uri', '-e', type=str, required=True,
        help='Full URI for etcd EX: http://127.0.0.1:2379')
    parser.add_argument(
        '--etcd-cert-path', '-C', type=str, required=False,
        help='Full path to the client side certificate.')
    parser.add_argument(
        '--etcd-cert-key-path', '-K', type=str, required=False,
        help='Full path to the client side certificate key.')
    parser.add_argument(
        '--etcd-ca-path', '-A', type=str, required=False,
        help='Full path to the CA file.')
    parser.add_argument(
        '--kube-uri', '-k', type=str, required=True,
        help='Full URI for kubernetes EX: http://127.0.0.1:8080')
    parser.add_argument(
        '--tls-keyfile', type=str, required=False,
        help='Full path to the TLS keyfile for the commissaire server')
    parser.add_argument(
        '--tls-certfile', type=str, required=False,
        help='Full path to the TLS certfile for the commissaire server')
    parser.add_argument(
        '--authentication-plugin', type=str, required=False,
        default='commissaire.authentication.httpauthbyetcd',
        help=('Authentication Plugin module. '
              'EX: commissaire.authentication.httpauth'))
    parser.add_argument(
        '--authentication-plugin-kwargs', type=str,
        required=False, default={},
        help='Authentication Plugin configuration (key=value)')

    args = parser.parse_args()

    try:
        config.etcd['uri'] = parse_uri(args.etcd_uri, 'etcd')
        config.kubernetes['uri'] = parse_uri(args.kube_uri, 'kube')
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        parser.error(ex)

    store_kwargs = {
        'read_timeout': 2,
        'host': config.etcd['uri'].hostname,
        'port': config.etcd['uri'].port,
        'protocol': config.etcd['uri'].scheme,
    }

    if bool(args.etcd_ca_path):
        config.etcd['certificate_ca_path'] = args.etcd_ca_path

    # We need all args to use a client side cert for etcd
    if bool(args.etcd_cert_path) ^ bool(args.etcd_cert_key_path):
        parser.error(
            'Both etcd-cert-path and etcd-cert-key-path must be '
            'provided to use a client side certificate with etcd.')
    elif bool(args.etcd_cert_path):
        if config.etcd['uri'].scheme != 'https':
            parser.error('An https URI is required when using '
                         'client side certificates.')
        store_kwargs['cert'] = (args.etcd_cert_path, args.etcd_cert_key_path)
        config.etcd['certificate_path'] = args.etcd_cert_path
        config.etcd['certificate_key_path'] = args.etcd_cert_key_path
        logging.info('Using client side certificate for etcd.')

    ds = etcd.Client(**store_kwargs)

    try:
        logging.config.dictConfig(
            json.loads(ds.read('/commissaire/config/logger').value))
        logging.info('Using Etcd for logging configuration.')
    except etcd.EtcdKeyNotFound:
        found_logger_config = False
        for logger_path in (
                '/etc/commissaire/logger.json', './conf/logger.json'):
            if os.path.isfile(logger_path):
                with open(logger_path, 'r') as logging_default_cfg:
                    logging.config.dictConfig(
                        json.loads(logging_default_cfg.read()))
                    found_logger_config = True
                logging.warn('No logger configuration in Etcd. Using defaults '
                             'at {0}'.format(logger_path))
        if not found_logger_config:
            parser.error(
                'Unable to find any logging configuration. Exiting ...')
    except etcd.EtcdConnectionFailed:
        _, ecf, _ = exception.raise_if_not(etcd.EtcdConnectionFailed)
        err = 'Unable to connect to Etcd: {0}. Exiting ...'.format(ecf)
        logging.fatal(err)
        parser.error('{0}\n'.format(err))
        raise SystemExit(1)

    # TLS options
    tls_keyfile = cli_etcd_or_default(
        'tlskeyfile', args.tls_keyfile, None, ds)
    tls_certfile = cli_etcd_or_default(
        'tlscertfile', args.tls_certfile, None, ds)

    interface = cli_etcd_or_default(
        'listeninterface', args.listen_interface, '0.0.0.0', ds)
    port = cli_etcd_or_default('listenport', args.listen_port, 8000, ds)

    # Pull options for accessing kubernetes
    try:
        config.kubernetes['token'] = ds.get(
            '/commissaire/config/kubetoken').value
        logging.info('Using kubetoken for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubetoken set.')
    try:
        config.kubernetes['certificate_path'] = ds.get(
            '/commissaire/config/kube_certificate_path').value
        config.kubernetes['certificate_key_path'] = ds.get(
            '/commissaire/config/kube_certificate_key_path').value
        logging.info('Using client side certificate for kubernetes.')
    except etcd.EtcdKeyNotFound:
        logging.debug('No kubernetes client side certificate set.')

    # Add our config instance to the cherrypy global config so we can use it's
    # values elsewhere
    # TODO: Technically this should be in the cherrypy.request.app.config
    # but it looks like that isn't accessable with WSGI based apps
    cherrypy.config['commissaire.config'] = config

    logging.debug('Config: {0}'.format(config))

    cherrypy.server.unsubscribe()
    # Disable autoreloading and use our logger
    cherrypy.config.update({'log.screen': False,
                            'log.access_file': '',
                            'log.error_file': '',
                            'engine.autoreload.on': False})

    server = cherrypy._cpserver.Server()
    server.socket_host = interface
    server.socket_port = int(port)
    server.thread_pool = 10

    if bool(tls_keyfile) ^ bool(tls_certfile):
        parser.error(
            'Both a keyfile and certfile must be '
            'given for commissaire server TLS. Exiting ...')
    if tls_keyfile and tls_certfile:
        server.ssl_module = 'builtin'
        server.ssl_certificate = tls_certfile
        server.ssl_private_key = tls_keyfile
        logging.info('Commissaire server TLS will be enabled.')
    server.subscribe()

    # Add our plugins
    CherryPyStorePlugin(cherrypy.engine, store_kwargs).subscribe()
    # NOTE: Anything that requires etcd should start AFTER
    # the engine is started
    cherrypy.engine.start()

    # Start processes
    PROCS['investigator'] = Process(
        target=investigator, args=(INVESTIGATE_QUEUE, config))
    PROCS['investigator'].start()

    try:
        # Make and mount the app
        authentication_kwargs = {}
        if '=' in args.authentication_plugin_kwargs:
            for item in args.authentication_plugin_kwargs.split(','):
                key, value = item.split('=')
                authentication_kwargs[key.strip()] = value.strip()
        app = create_app(
            ds,
            args.authentication_plugin,
            authentication_kwargs)
        cherrypy.tree.graft(app, "/")

        # Serve forever
        cherrypy.engine.block()
    except Exception:
        _, ex, _ = exception.raise_if_not(Exception)
        logging.fatal('Unable to start server: {0}'.format(ex))
        cherrypy.engine.stop()

    PROCS['investigator'].terminate()
    PROCS['investigator'].join()