diff --git a/0016-Use-TLS-on-revocation-notification-webhook.patch b/0016-Use-TLS-on-revocation-notification-webhook.patch new file mode 100644 index 0000000..6544844 --- /dev/null +++ b/0016-Use-TLS-on-revocation-notification-webhook.patch @@ -0,0 +1,167 @@ +From 4bd644b74719fdbb6c521d3d5eb2430d8dc18b36 Mon Sep 17 00:00:00 2001 +From: Sergio Correia +Date: Wed, 5 Feb 2025 16:16:25 +0000 +Subject: [PATCH 16/16] Use TLS on revocation notification webhook + +--- + keylime/requests_client.py | 5 ++ + keylime/revocation_notifier.py | 91 +++++++++++++++++++++++----------- + 2 files changed, 68 insertions(+), 28 deletions(-) + +diff --git a/keylime/requests_client.py b/keylime/requests_client.py +index 85a175c..e993fbc 100644 +--- a/keylime/requests_client.py ++++ b/keylime/requests_client.py +@@ -1,3 +1,4 @@ ++import re + import ssl + from typing import Any, Dict, Optional + +@@ -15,6 +16,10 @@ class RequestsClient: + ignore_hostname: bool = True, + **kwargs: Any, + ) -> None: ++ # Remove eventual "http?://" from the base url ++ if base_url.startswith("http"): ++ base_url = re.sub(r"https?://", "", base_url) ++ + if tls_enabled: + self.base_url = f"https://{base_url}" + else: +diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py +index 5cc8b1a..434bf64 100644 +--- a/keylime/revocation_notifier.py ++++ b/keylime/revocation_notifier.py +@@ -9,8 +9,9 @@ from typing import Any, Callable, Dict, Optional, Set + + import requests + +-from keylime import config, crypto, json, keylime_logging ++from keylime import config, crypto, json, keylime_logging, web_util + from keylime.common import retry ++from keylime.requests_client import RequestsClient + + logger = keylime_logging.init_logging("revocation_notifier") + broker_proc: Optional[Process] = None +@@ -109,7 +110,10 @@ def notify(tosend: Dict[str, Any]) -> None: + exponential_backoff = config.getboolean("verifier", "exponential_backoff") + next_retry = retry.retry_time(exponential_backoff, interval, i, logger) + logger.debug( +- "Unable to publish revocation message %d times, trying again in %f seconds: %s", i, next_retry, e ++ "Unable to publish revocation message %d times, trying again in %f seconds: %s", ++ i, ++ next_retry, ++ e, + ) + time.sleep(next_retry) + mysock.close() +@@ -132,30 +136,50 @@ def notify_webhook(tosend: Dict[str, Any]) -> None: + def worker_webhook(tosend: Dict[str, Any], url: str) -> None: + interval = config.getfloat("verifier", "retry_interval") + exponential_backoff = config.getboolean("verifier", "exponential_backoff") +- with requests.Session() as session: +- logger.info("Sending revocation event via webhook...") +- for i in range(config.getint("verifier", "max_retries")): +- next_retry = retry.retry_time(exponential_backoff, interval, i, logger) ++ ++ max_retries = config.getint("verifier", "max_retries") ++ if max_retries <= 0: ++ logger.info("Invalid value found in 'max_retries' option for verifier, using default value") ++ max_retries = 5 ++ ++ # Get TLS options from the configuration ++ (cert, key, trusted_ca, key_password), verify_server_cert = web_util.get_tls_options( ++ "verifier", is_client=True, logger=logger ++ ) ++ ++ # Generate the TLS context using the obtained options ++ tls_context = web_util.generate_tls_context(cert, key, trusted_ca, key_password, is_client=True, logger=logger) ++ ++ logger.info("Sending revocation event via webhook to %s ...", url) ++ for i in range(max_retries): ++ next_retry = retry.retry_time(exponential_backoff, interval, i, logger) ++ ++ with RequestsClient( ++ url, ++ verify_server_cert, ++ tls_context, ++ ) as client: + try: +- response = session.post(url, json=tosend, timeout=5) +- if response.status_code in [200, 202]: +- break +- +- logger.debug( +- "Unable to publish revocation message %d times via webhook, " +- "trying again in %d seconds. " +- "Server returned status code: %s", +- i, +- next_retry, +- response.status_code, +- ) +- except requests.exceptions.RequestException as e: +- logger.debug( +- "Unable to publish revocation message %d times via webhook, trying again in %d seconds: %s", +- i, +- next_retry, +- e, +- ) ++ res = client.post("", json=tosend, timeout=5) ++ except requests.exceptions.SSLError as ssl_error: ++ if "TLSV1_ALERT_UNKNOWN_CA" in str(ssl_error): ++ logger.warning( ++ "Keylime does not recognize certificate from peer. Check if verifier 'trusted_server_ca' is configured correctly" ++ ) ++ ++ raise ssl_error from ssl_error ++ ++ if res and res.status_code in [200, 202]: ++ break ++ ++ logger.debug( ++ "Unable to publish revocation message %d times via webhook, " ++ "trying again in %d seconds. " ++ "Server returned status code: %s", ++ i + 1, ++ next_retry, ++ res.status_code, ++ ) + + time.sleep(next_retry) + +@@ -167,7 +191,11 @@ def notify_webhook(tosend: Dict[str, Any]) -> None: + cert_key = None + + +-def process_revocation(revocation: Dict[str, Any], callback: Callable[[Dict[str, Any]], None], cert_path: str) -> None: ++def process_revocation( ++ revocation: Dict[str, Any], ++ callback: Callable[[Dict[str, Any]], None], ++ cert_path: str, ++) -> None: + global cert_key + + if cert_key is None: +@@ -179,10 +207,17 @@ def process_revocation(revocation: Dict[str, Any], callback: Callable[[Dict[str, + cert_key = crypto.x509_import_pubkey(certpem) + + if cert_key is None: +- logger.warning("Unable to check signature of revocation message: %s not available", cert_path) ++ logger.warning( ++ "Unable to check signature of revocation message: %s not available", ++ cert_path, ++ ) + elif "signature" not in revocation or revocation["signature"] == "none": + logger.warning("No signature on revocation message from server") +- elif not crypto.rsa_verify(cert_key, revocation["msg"].encode("utf-8"), revocation["signature"].encode("utf-8")): ++ elif not crypto.rsa_verify( ++ cert_key, ++ revocation["msg"].encode("utf-8"), ++ revocation["signature"].encode("utf-8"), ++ ): + logger.error("Invalid revocation message siganture %s", revocation) + else: + message = json.loads(revocation["msg"]) +-- +2.47.1 + diff --git a/keylime.spec b/keylime.spec index d9a69d7..792f10a 100644 --- a/keylime.spec +++ b/keylime.spec @@ -9,7 +9,7 @@ Name: keylime Version: 7.3.0 -Release: 14%{?dist} +Release: 15%{?dist} Summary: Open source TPM software for Bootstrapping and Maintaining Trust URL: https://github.com/keylime/keylime @@ -32,6 +32,7 @@ Patch: 0012-Restore-create-allowlist.patch Patch: 0013-Set-generator-and-timestamp-in-create-policy.patch Patch: 0014-tpm_util-Replace-a-logger.error-with-an-Exception-in.patch Patch: 0015-Backport-keylime-policy-tool.patch +Patch: 0016-Use-TLS-on-revocation-notification-webhook.patch License: ASL 2.0 and MIT @@ -374,6 +375,16 @@ fi %license LICENSE %changelog +* Wed Feb 05 2025 Sergio Correia - 7.3.0-15 +- Use TLS on revocation notification webhook +- Include system installed CA certificates when verifying webhook + server certificate +- Include the CA certificates added via configuration file option + 'trusted_server_ca' + Resolves: RHEL-78057 + Resolves: RHEL-78313 + Resolves: RHEL-78316 + * Fri Jan 10 2025 Sergio Correia - 7.3.0-14 - Backport keylime-policy tool Resolves: RHEL-75797