From 15ec41729106c06703ab47f762d9aff4742b264e Mon Sep 17 00:00:00 2001 From: Jan-Frode Myklebust Date: Sat, 19 Nov 2022 00:29:00 +0100 Subject: [PATCH] Encrypt kmip-server database using sqlcipher3. Database will be encrypted by default, with password "pykmip". Password can be configured with the database_password in /etc/pykmip/server.conf. --- docs/source/server.rst | 5 +++++ kmip/services/server/config.py | 18 +++++++++++++++++- kmip/services/server/engine.py | 8 +++++--- kmip/services/server/server.py | 14 ++++++++++---- requirements.txt | 1 + 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/docs/source/server.rst b/docs/source/server.rst index df0126e8..7a92099b 100644 --- a/docs/source/server.rst +++ b/docs/source/server.rst @@ -38,6 +38,7 @@ as found in the configuration file, is shown below: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 logging_level=DEBUG database_path=/tmp/pykmip.db + database_password=pykmip The server can also be configured manually via Python. The following example shows how to create the ``KmipServer`` in Python code, directly specifying the @@ -64,6 +65,7 @@ different configuration values: ... ], ... logging_level='DEBUG', ... database_path='/tmp/pykmip.db' + ... database_password='pykmip' ... ) The different configuration options are defined below: @@ -125,6 +127,9 @@ The different configuration options are defined below: * ``database_path`` A string representing a path to a SQLite database file. The server will store all managed objects (e.g., keys, certificates) in this file. +* ``database_password`` + A string representing the password to encryot the SQLite database file. + Defaults to ``pykmip``. .. note:: When installing PyKMIP and deploying the server, you must manually set up diff --git a/kmip/services/server/config.py b/kmip/services/server/config.py index 912d980d..965bc0f0 100644 --- a/kmip/services/server/config.py +++ b/kmip/services/server/config.py @@ -52,7 +52,8 @@ def __init__(self): 'enable_tls_client_auth', 'tls_cipher_suites', 'logging_level', - 'database_path' + 'database_path', + 'database_password' ] def set_setting(self, setting, value): @@ -96,6 +97,8 @@ def set_setting(self, setting, value): self._set_tls_cipher_suites(value) elif setting == 'logging_level': self._set_logging_level(value) + elif setting == 'database_password': + self._set_database_password(value) else: self._set_database_path(value) @@ -182,6 +185,9 @@ def _parse_settings(self, parser): self._set_logging_level( parser.get('server', 'logging_level') ) + if parser.has_option('server', 'database_password'): + self._set_database_password(parser.get('server', 'database_password')) + if parser.has_option('server', 'database_path'): self._set_database_path(parser.get('server', 'database_path')) @@ -350,3 +356,13 @@ def _set_database_path(self, value): "The database path, if specified, must be a valid path to a " "SQLite database file." ) + + def _set_database_password(self, value): + if not value: + self.settings['database_password'] = None + elif isinstance(value, six.string_types): + self.settings['database_password'] = value + else: + raise exceptions.ConfigurationError( + "The database password is an invalid string." + ) diff --git a/kmip/services/server/engine.py b/kmip/services/server/engine.py index 4c5ff508..67a29cf3 100644 --- a/kmip/services/server/engine.py +++ b/kmip/services/server/engine.py @@ -17,6 +17,7 @@ import logging import six import sqlalchemy +import sqlcipher3 from sqlalchemy.orm import exc @@ -72,7 +73,7 @@ class KmipEngine(object): * Cryptographic usage mask enforcement per object type """ - def __init__(self, policies=None, database_path=None): + def __init__(self, policies=None, database_path=None, database_password=None): """ Create a KmipEngine. @@ -83,14 +84,15 @@ def __init__(self, policies=None, database_path=None): database_path (string): The path to the SQLite database file used to store all server data. Optional, defaults to None. If none, database path defaults to '/tmp/pykmip.database'. + database_password (string): The password to the SQLite database file. """ self._logger = logging.getLogger('kmip.server.engine') self._cryptography_engine = engine.CryptographyEngine() - self.database_path = 'sqlite:///{}'.format(database_path) + self.database_path = 'sqlite+pysqlcipher://:{database_password}@/{database_path}'.format(database_password = database_password, database_path = database_path) if not database_path: - self.database_path = 'sqlite:////tmp/pykmip.database' + self.database_path = 'sqlite+pysqlcipher://:{database_password}@//tmp/pykmip.database'.format(database_password = database_password) self._data_store = sqlalchemy.create_engine( self.database_path, diff --git a/kmip/services/server/server.py b/kmip/services/server/server.py index 534ab61d..386dc15a 100644 --- a/kmip/services/server/server.py +++ b/kmip/services/server/server.py @@ -61,7 +61,8 @@ def __init__( tls_cipher_suites=None, logging_level=None, live_policies=False, - database_path=None + database_path=None, + database_password='pykmip' ): """ Create a KmipServer. @@ -127,6 +128,8 @@ def __init__( to False. database_path (string): The path to the server's SQLite database file. Optional, defaults to None. + database_password (string): Password to encrypt the SQLite database + file. Optional, defaults to "pykmip". """ self._logger = logging.getLogger('kmip.server') self._setup_logging(log_path) @@ -144,7 +147,8 @@ def __init__( enable_tls_client_auth, tls_cipher_suites, logging_level, - database_path + database_path, + database_password ) self.live_policies = live_policies self.policies = {} @@ -194,7 +198,8 @@ def _setup_configuration( enable_tls_client_auth=None, tls_cipher_suites=None, logging_level=None, - database_path=None + database_path=None, + database_password="pykmip" ): if path: self.config.load_settings(path) @@ -261,7 +266,8 @@ def interrupt_handler(trigger, frame): self._engine = engine.KmipEngine( policies=self.policies, - database_path=self.config.settings.get('database_path') + database_path=self.config.settings.get('database_path'), + database_password=self.config.settings.get('database_password') ) self._logger.info("Starting server socket handler.") diff --git a/requirements.txt b/requirements.txt index bee04c94..68b947ce 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ enum-compat requests six>=1.11.0 sqlalchemy>=1.0 +sqlcipher3