Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package keylime for openSUSE:Factory checked in at 2022-01-21 01:24:58 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/keylime (Old) and /work/SRC/openSUSE:Factory/.keylime.new.1938 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "keylime" Fri Jan 21 01:24:58 2022 rev:11 rq:947243 version:6.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/keylime/keylime.changes 2022-01-13 00:22:14.719924836 +0100 +++ /work/SRC/openSUSE:Factory/.keylime.new.1938/keylime.changes 2022-01-21 01:25:02.742741216 +0100 @@ -1,0 +2,9 @@ +Tue Jan 18 14:28:05 UTC 2022 - Alberto Planas Dominguez <apla...@suse.com> + +- Add 0001-config-support-merge-multiple-config-files.patch + This will allow the merge of config files in /usr/etc and /etc. +- Move the configuration file to /usr/etc in new distributions +- Add 0001-ca-support-back-old-cyptography-API.patch + This is only required for SLE, but the API is compatible with new versions + +------------------------------------------------------------------- New: ---- 0001-ca-support-back-old-cyptography-API.patch 0001-config-support-merge-multiple-config-files.patch ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ keylime.spec ++++++ --- /var/tmp/diff_new_pack.h2NGLR/_old 2022-01-21 01:25:03.790734031 +0100 +++ /var/tmp/diff_new_pack.h2NGLR/_new 2022-01-21 01:25:03.794734005 +0100 @@ -40,6 +40,10 @@ Patch3: config-libefivars.diff # PATCH-FIX-UPSTREAM 0001-Drop-dataclasses-module-usage.patch (gh#keylime/keylime!827) Patch4: 0001-Drop-dataclasses-module-usage.patch +# PATCH-FIX-UPSTREAM 0001-config-support-merge-multiple-config-files.patch (gh#keylime/keylime!829) +Patch5: 0001-config-support-merge-multiple-config-files.patch +# PATCH-FIX-UPSTREAM 0001-ca-support-back-old-cyptography-API.patch (gh#keylime/keylime!839) +Patch6: 0001-ca-support-back-old-cyptography-API.patch BuildRequires: %{python_module setuptools} BuildRequires: fdupes BuildRequires: firewall-macros @@ -154,7 +158,13 @@ %python_expand %fdupes %{buildroot}%{$python_sitelib} +%if 0%{?suse_version} >= 1550 +# setup.py copy keylime.conf in /etc, but we expect it in /usr/etc +rm %{buildroot}%{_sysconfdir}/%{srcname}.conf +install -Dpm 600 %{srcname}.conf %{buildroot}%{_prefix}%{_sysconfdir}/%{srcname}.conf +%else install -Dpm 600 %{srcname}.conf %{buildroot}%{_sysconfdir}/%{srcname}.conf +%endif install -Dpm 644 ./services/%{srcname}_agent.service %{buildroot}%{_unitdir}/%{srcname}_agent.service install -Dpm 644 ./services/%{srcname}_verifier.service %{buildroot}%{_unitdir}/%{srcname}_verifier.service install -Dpm 644 ./services/%{srcname}_registrar.service %{buildroot}%{_unitdir}/%{srcname}_registrar.service @@ -253,7 +263,11 @@ %{python_sitelib}/* %files -n %{srcname}-config +%if 0%{?suse_version} >= 1550 +%{_prefix}%{_sysconfdir}/%{srcname}.conf +%else %config(noreplace) %{_sysconfdir}/%{srcname}.conf +%endif %files -n %{srcname}-firewalld %dir %{_prefix}/lib/firewalld ++++++ 0001-ca-support-back-old-cyptography-API.patch ++++++ >From 57d033e9a9a5946a63c9d161381dee4830017531 Mon Sep 17 00:00:00 2001 From: Alberto Planas <apla...@suse.com> Date: Tue, 18 Jan 2022 15:04:55 +0100 Subject: [PATCH] ca: support back old cyptography API After [1] we moved from M2Crypto to cryptography, and we started to drop the use of the, now optional, `backend` parameter. This is OK as we are explicit on the minimal version of cryptography, but we still use in other modules (like crypto.py or tpm_main.py for example) the old API. This patch use the optional `backend` parameter to unify the API usage, and still be compatible with some older versions of cryptography. [1] https://github.com/keylime/keylime/pull/747 Signed-off-by: Alberto Planas <apla...@suse.com> --- keylime/ca_impl_cfssl.py | 2 ++ keylime/ca_impl_openssl.py | 4 ++++ keylime/ca_util.py | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) Index: keylime-v6.2.1/keylime/ca_impl_cfssl.py =================================================================== --- keylime-v6.2.1.orig/keylime/ca_impl_cfssl.py +++ keylime-v6.2.1/keylime/ca_impl_cfssl.py @@ -127,6 +127,7 @@ def mk_cacert(name=None): privkey = serialization.load_pem_private_key( body['result']['private_key'].encode('utf-8'), password=None, + backend=default_backend(), ) cert = x509.load_pem_x509_certificate( data=body['result']['certificate'].encode('utf-8'), @@ -212,6 +213,7 @@ def mk_signed_cert(cacert, ca_pk, name, pk = serialization.load_pem_private_key( body['result']['private_key'].encode('utf-8'), password=None, + backend=default_backend(), ) cert = x509.load_pem_x509_certificate( data=body['result']['certificate'].encode('utf-8'), Index: keylime-v6.2.1/keylime/ca_impl_openssl.py =================================================================== --- keylime-v6.2.1.orig/keylime/ca_impl_openssl.py +++ keylime-v6.2.1/keylime/ca_impl_openssl.py @@ -6,6 +6,7 @@ Copyright 2017 Massachusetts Institute o import datetime from cryptography import x509 +from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes from cryptography.hazmat.primitives.asymmetric import rsa from cryptography.x509.oid import NameOID @@ -63,6 +64,7 @@ def mk_request(bits, common_name): privkey = rsa.generate_private_key( public_exponent=65537, key_size=bits, + backend=default_backend(), ) cert_req = x509.CertificateBuilder() @@ -128,6 +130,7 @@ def mk_cacert(name=None): cert = cert_req.sign( private_key=privkey, algorithm=hashes.SHA256(), + backend=default_backend(), ) return cert, privkey, pubkey @@ -177,6 +180,7 @@ def mk_signed_cert(cacert, ca_privkey, n cert = cert_req.sign( private_key=ca_privkey, algorithm=hashes.SHA256(), + backend=default_backend(), ) return cert, privkey Index: keylime-v6.2.1/keylime/ca_util.py =================================================================== --- keylime-v6.2.1.orig/keylime/ca_util.py +++ keylime-v6.2.1/keylime/ca_util.py @@ -88,7 +88,11 @@ def cmd_mkcert(workingdir, name): config.ch_dir(workingdir, logger) priv = read_private() cacert = load_cert_by_path('cacert.crt') - ca_pk = serialization.load_pem_private_key(priv[0]['ca'], password=None) + ca_pk = serialization.load_pem_private_key( + priv[0]['ca'], + password=None, + backend=default_backend() + ) cert, pk = ca_impl.mk_signed_cert( cacert, ca_pk, name, priv[0]['lastserial'] + 1) ++++++ 0001-config-support-merge-multiple-config-files.patch ++++++ >From b6879e3e3c238609a7f001b2536c699c63f050f9 Mon Sep 17 00:00:00 2001 From: Alberto Planas <apla...@suse.com> Date: Wed, 12 Jan 2022 15:42:56 +0100 Subject: [PATCH] config: support merge multiple config files Currently Keylime read the config for all the services from the same file, in /etc/keylime.conf. This commit support the load of multiple configuration files, and merge the contents together. This will enable the separation of a system wide configuration file in /usr/etc/keylime.conf, and a admin owerwrite in /etc/keylime.conf. Signed-off-by: Alberto Planas <apla...@suse.com> --- keylime/cloud_verifier_common.py | 2 +- keylime/cloud_verifier_tornado.py | 8 +-- keylime/config.py | 93 +++++++++++-------------------- keylime/ima_ast.py | 2 +- keylime/keylime_logging.py | 4 +- keylime/revocation_notifier.py | 2 +- test/data/config/keylime-1.conf | 3 + test/data/config/keylime-2.conf | 3 + test/data/config/keylime-3.conf | 4 ++ test/test_config.py | 82 +++++++++++++++++++++++++++ 10 files changed, 133 insertions(+), 70 deletions(-) create mode 100644 test/data/config/keylime-1.conf create mode 100644 test/data/config/keylime-2.conf create mode 100644 test/data/config/keylime-3.conf create mode 100644 test/test_config.py Index: keylime-v6.2.1/keylime/cloud_verifier_common.py =================================================================== --- keylime-v6.2.1.orig/keylime/cloud_verifier_common.py +++ keylime-v6.2.1/keylime/cloud_verifier_common.py @@ -368,7 +368,7 @@ def process_get_status(agent): def notify_error(agent, msgtype='revocation', event=None): send_mq = config.getboolean('cloud_verifier', 'revocation_notifier') - send_webhook = config.getboolean('cloud_verifier', 'revocation_notifier_webhook', False) + send_webhook = config.getboolean('cloud_verifier', 'revocation_notifier_webhook', fallback=False) if not (send_mq or send_webhook): return Index: keylime-v6.2.1/keylime/cloud_verifier_tornado.py =================================================================== --- keylime-v6.2.1.orig/keylime/cloud_verifier_tornado.py +++ keylime-v6.2.1/keylime/cloud_verifier_tornado.py @@ -330,7 +330,7 @@ class AgentsHandler(BaseHandler): logger.info('DELETE returning 404 response. agent id: %s not found.', agent_id) return - verifier_id = config.get('cloud_verifier', 'cloudverifier_id', cloud_verifier_common.DEFAULT_VERIFIER_ID) + verifier_id = config.get('cloud_verifier', 'cloudverifier_id', fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID) if verifier_id != agent.verifier_id: config.echo_json_response(self, 404, "agent id associated to this verifier") logger.info('DELETE returning 404 response. agent id: %s not associated to this verifer.', agent_id) @@ -417,7 +417,7 @@ class AgentsHandler(BaseHandler): agent_data['pcr10'] = START_HASH agent_data['next_ima_ml_entry'] = 0 agent_data['learned_ima_keyrings'] = {} - agent_data['verifier_id'] = config.get('cloud_verifier', 'cloudverifier_id', cloud_verifier_common.DEFAULT_VERIFIER_ID) + agent_data['verifier_id'] = config.get('cloud_verifier', 'cloudverifier_id', fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID) agent_data['verifier_ip'] = config.get('cloud_verifier', 'cloudverifier_ip') agent_data['verifier_port'] = config.get('cloud_verifier', 'cloudverifier_port') @@ -492,7 +492,7 @@ class AgentsHandler(BaseHandler): config.echo_json_response(self, 400, "uri not supported") logger.warning("PUT returning 400 response. uri not supported") try: - verifier_id = config.get('cloud_verifier', 'cloudverifier_id', cloud_verifier_common.DEFAULT_VERIFIER_ID) + verifier_id = config.get('cloud_verifier', 'cloudverifier_id', fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID) agent = session.query(VerfierMain).filter_by( agent_id=agent_id, verifier_id=verifier_id).one() except SQLAlchemyError as e: @@ -971,7 +971,7 @@ def main(): cloudverifier_port = config.get('cloud_verifier', 'cloudverifier_port') cloudverifier_host = config.get('cloud_verifier', 'cloudverifier_ip') - cloudverifier_id = config.get('cloud_verifier', 'cloudverifier_id', cloud_verifier_common.DEFAULT_VERIFIER_ID) + cloudverifier_id = config.get('cloud_verifier', 'cloudverifier_id', fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID) # allow tornado's max upload size to be configurable max_upload_size = None Index: keylime-v6.2.1/keylime/config.py =================================================================== --- keylime-v6.2.1.orig/keylime/config.py +++ keylime-v6.2.1/keylime/config.py @@ -6,7 +6,6 @@ Copyright 2017 Massachusetts Institute o import os import os.path import configparser -import sys import urllib.parse import re from http.server import BaseHTTPRequestHandler @@ -117,69 +116,39 @@ TPM_LIBS_PATH = '/usr/local/lib/' TPM_TOOLS_PATH = '/usr/local/bin/' -CONFIG_FILE = os.getenv('KEYLIME_CONFIG', '/etc/keylime.conf') - - -WARN = False -if not os.path.exists(CONFIG_FILE): - # try to locate the config file next to the script if bundled - if getattr(sys, 'frozen', False): - CONFIG_FILE = os.path.dirname( - os.path.abspath(sys.executable)) + "/keylime.conf" - else: - # instead try to get config file from python data_files install - CONFIG_FILE = os.path.dirname(os.path.abspath( - __file__)) + "/../package_default/keylime.conf" - WARN = True - -if not os.path.exists(CONFIG_FILE): - raise Exception(f"{CONFIG_FILE} does not exist. Please set environment" - f"variable KEYLIME_CONFIG or see {__file__} for more" - f"details") -print(f"Using config file {CONFIG_FILE}") -if WARN: - print("WARNING: Keylime is using the config file from its installation location. \n\tWe recommend you copy keylime.conf to /etc/ to customize it.") - - -_CURRENT_CONFIG = None +# Config files can be merged together, reading from the system to the +# user. +CONFIG_FILES = [ + os.path.expanduser("~/.config/keylime.conf"), "/etc/keylime.conf", "/usr/etc/keylime.conf" +] +if "KEYLIME_CONFIG" in os.environ: + CONFIG_FILES.insert(0, os.environ["KEYLIME_CONFIG"]) def get_config(): - global _CURRENT_CONFIG - if _CURRENT_CONFIG is None: - # read the config file - _CURRENT_CONFIG = configparser.ConfigParser() - _CURRENT_CONFIG.read(CONFIG_FILE) - return _CURRENT_CONFIG - - -def get(section, option, fallback=None): - if fallback is not None: - return get_config().get(section, option, fallback=fallback) - return get_config().get(section, option) - - -def getint(section, option, fallback=None): - if fallback is not None: - return get_config().get(section, option, fallback=fallback) - return get_config().getint(section, option) - - -def getboolean(section, option, fallback=None): - if fallback is not None: - return get_config().getboolean(section, option, fallback=fallback) - return get_config().getboolean(section, option) - - -def getfloat(section, option, fallback=None): - if fallback is not None: - return get_config().get(section, option, fallback=fallback) - return get_config().getfloat(section, option) - - -def has_option(section, option): - return get_config().has_option(section, option) - + """Read configuration files and merge them together.""" + if not getattr(get_config, "config", None): + # TODO - use logger and be sure that all variables have a + # propper default, and the sections are initialized + if not any(os.path.exists(c) for c in CONFIG_FILES): + print(f"Config file not found in {CONFIG_FILES}. Please set " + f"environment variable KEYLIME_CONFIG or see {__file__} " + "for more details") + + # Validate that at least one config file is present + get_config.config = configparser.ConfigParser() + config_files = get_config.config.read(CONFIG_FILES) + # TODO - use the logger + print(f"Reading configuration from {config_files}") + return get_config.config + + +# Re-export some utility functions +get = get_config().get +getint = get_config().getint +getboolean = get_config().getboolean +getfloat = get_config().getfloat +has_option = get_config().has_option if not REQUIRE_ROOT: DEFAULT_WORK_DIR = os.path.abspath(".") @@ -319,6 +288,6 @@ TPM_DATA_PCR = 16 BOOTSTRAP_KEY_SIZE = 32 # choose between cfssl or openssl for creating CA certificates -CA_IMPL = get_config().get('general', 'ca_implementation') +CA_IMPL = get_config().get('general', 'ca_implementation', fallback='openssl') CRL_PORT = 38080 Index: keylime-v6.2.1/keylime/ima_ast.py =================================================================== --- keylime-v6.2.1.orig/keylime/ima_ast.py +++ keylime-v6.2.1/keylime/ima_ast.py @@ -351,7 +351,7 @@ class Entry: if self.template_hash == FF_HASH: logger.warning("Skipped template_hash validation entry with FF_HASH") # By default ToMToU errors are not treated as a failure - if config.getboolean("cloud_verifier", "tomtou_errors", False): + if config.getboolean("cloud_verifier", "tomtou_errors", fallback=False): failure.add_event("tomtou", "hash validation was skipped", True) return failure if self.template_hash != self.mode.hash(): Index: keylime-v6.2.1/keylime/keylime_logging.py =================================================================== --- keylime-v6.2.1.orig/keylime/keylime_logging.py +++ keylime-v6.2.1/keylime/keylime_logging.py @@ -19,7 +19,9 @@ if not config.REQUIRE_ROOT: else: LOGSTREAM = LOGDIR + '/keylime-stream.log' -logging.config.fileConfig(config.CONFIG_FILE) +for config_file in reversed(config.CONFIG_FILES): + if os.path.exists(config_file): + logging.config.fileConfig(config_file) def set_log_func(loglevel, logger): Index: keylime-v6.2.1/keylime/revocation_notifier.py =================================================================== --- keylime-v6.2.1.orig/keylime/revocation_notifier.py +++ keylime-v6.2.1/keylime/revocation_notifier.py @@ -81,7 +81,7 @@ def notify(tosend): def notify_webhook(tosend): - url = config.get('cloud_verifier', 'webhook_url', '') + url = config.get('cloud_verifier', 'webhook_url', fallback='') # Check if a url was specified if url == '': return Index: keylime-v6.2.1/test/data/config/keylime-1.conf =================================================================== --- /dev/null +++ keylime-v6.2.1/test/data/config/keylime-1.conf @@ -0,0 +1,3 @@ +[default] + +attribute_1 = value_1 Index: keylime-v6.2.1/test/data/config/keylime-2.conf =================================================================== --- /dev/null +++ keylime-v6.2.1/test/data/config/keylime-2.conf @@ -0,0 +1,3 @@ +[default] + +attribute_2= value_2 Index: keylime-v6.2.1/test/data/config/keylime-3.conf =================================================================== --- /dev/null +++ keylime-v6.2.1/test/data/config/keylime-3.conf @@ -0,0 +1,4 @@ +[default] + +attribute_1 = value_1_3 +attribute_3 = value_3 Index: keylime-v6.2.1/test/test_config.py =================================================================== --- /dev/null +++ keylime-v6.2.1/test/test_config.py @@ -0,0 +1,82 @@ +import importlib +import os +import unittest + +from keylime import config + +CONFIG_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), "data/config")) + + +class TestConfig(unittest.TestCase): + def setUp(self): + """The config module should be reloaded.""" + # Because we can alter global state, we should reload the + # config module before every test + importlib.reload(config) + # Remove a side effect of some variables pre-loading the + # configuration files + del config.get_config.config + + def test_default_config_files(self): + """Test default config file list.""" + self.assertEqual( + config.CONFIG_FILES, + [ + os.path.expanduser("~/.config/keylime.conf"), + "/etc/keylime.conf", + "/usr/etc/keylime.conf", + ], + ) + + def test_no_config(self): + """Test that no config files is an empty set.""" + c = config.get_config() + self.assertEqual(c.get("default", "value_1", fallback=None), None) + + def test_single_config(self): + """Test reading a single config file.""" + config.CONFIG_FILES = [os.path.join(CONFIG_DIR, "keylime-1.conf")] + c = config.get_config() + self.assertEqual(c.get("default", "attribute_1"), "value_1") + + def test_multiple_config(self): + """Test reading multiple config files.""" + config.CONFIG_FILES = [ + os.path.join(CONFIG_DIR, "keylime-1.conf"), + os.path.join(CONFIG_DIR, "keylime-2.conf"), + ] + c = config.get_config() + self.assertEqual(c.get("default", "attribute_1"), "value_1") + self.assertEqual(c.get("default", "attribute_2"), "value_2") + + def test_merge_config(self): + """Test reading multiple config files and merging them.""" + config.CONFIG_FILES = [ + os.path.join(CONFIG_DIR, "keylime-1.conf"), + os.path.join(CONFIG_DIR, "keylime-2.conf"), + os.path.join(CONFIG_DIR, "keylime-3.conf"), + ] + c = config.get_config() + self.assertEqual(c.get("default", "attribute_1"), "value_1_3") + self.assertEqual(c.get("default", "attribute_2"), "value_2") + self.assertEqual(c.get("default", "attribute_3"), "value_3") + + def test_cache_config(self): + """Test the config is properly cached between calls.""" + c = config.get_config() + self.assertEqual(c.get("default", "attribute", fallback=None), None) + + c.add_section("default") + c.set("default", "attribute", "value") + self.assertEqual(c.get("default", "attribute"), "value") + + c_copy = config.get_config() + self.assertEqual(c_copy.get("default", "attribute"), "value") + + def test_reexport_function(self): + """Test re-exported functions to access data.""" + self.assertEqual(config.get("default", "attribute", fallback=None), None) + + +if __name__ == "__main__": + unittest.main()