Add keylime to RHEL-9
Resolves: rhbz#2082989
This commit is contained in:
parent
9c72dfea97
commit
b19c921a82
1
.gitignore
vendored
1
.gitignore
vendored
@ -0,0 +1 @@
|
|||||||
|
/v6.4.1.tar.gz
|
@ -0,0 +1,39 @@
|
|||||||
|
From 90811cc0df4f32fbf9e5389cca15813e2f6395cb Mon Sep 17 00:00:00 2001
|
||||||
|
From: Sergio Correia <scorreia@redhat.com>
|
||||||
|
Date: Fri, 3 Jun 2022 22:01:15 -0300
|
||||||
|
Subject: [PATCH 1/5] Improve error handling when doing signature verification
|
||||||
|
|
||||||
|
This makes verify_signature_from_file() more consistent in that it will
|
||||||
|
always raise an exception informing the signature verification failed,
|
||||||
|
when this situation happens.
|
||||||
|
|
||||||
|
As it is, verify_signature() can raise a few different exceptions, and
|
||||||
|
those were not handled by verify_signature_from_file().
|
||||||
|
|
||||||
|
Signed-off-by: Sergio Correia <scorreia@redhat.com>
|
||||||
|
---
|
||||||
|
keylime/signing.py | 8 +++++++-
|
||||||
|
1 file changed, 7 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/keylime/signing.py b/keylime/signing.py
|
||||||
|
index 71f8be0..1353c1e 100644
|
||||||
|
--- a/keylime/signing.py
|
||||||
|
+++ b/keylime/signing.py
|
||||||
|
@@ -30,7 +30,13 @@ def verify_signature_from_file(key_file, filename, sig_file, file_description):
|
||||||
|
with open(filename, "rb") as file_f:
|
||||||
|
file = file_f.read()
|
||||||
|
|
||||||
|
- if verify_signature(key, sig, file):
|
||||||
|
+ verified = False
|
||||||
|
+ try:
|
||||||
|
+ verified = verify_signature(key, sig, file)
|
||||||
|
+ except Exception as e:
|
||||||
|
+ logger.warning("Unable to verify signature: %s", e)
|
||||||
|
+
|
||||||
|
+ if verified:
|
||||||
|
logger.debug("%s passed signature verification", file_description.capitalize())
|
||||||
|
else:
|
||||||
|
raise Exception(
|
||||||
|
--
|
||||||
|
2.35.1
|
||||||
|
|
@ -0,0 +1,88 @@
|
|||||||
|
From 09db3fe88b22c0e1522343c14f184ea610883fcf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Daiki Ueno <dueno@redhat.com>
|
||||||
|
Date: Mon, 21 Mar 2022 11:06:45 +0100
|
||||||
|
Subject: [PATCH 2/5] revocation_notifier: Factor out revocation message
|
||||||
|
processing
|
||||||
|
|
||||||
|
This moves the revocation message processing logic out of
|
||||||
|
await_notifications, so it can be called directly from the POST
|
||||||
|
handler.
|
||||||
|
|
||||||
|
Signed-off-by: Daiki Ueno <dueno@redhat.com>
|
||||||
|
---
|
||||||
|
keylime/revocation_notifier.py | 45 ++++++++++++++++++----------------
|
||||||
|
1 file changed, 24 insertions(+), 21 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py
|
||||||
|
index 4854a99..7cfe0e5 100644
|
||||||
|
--- a/keylime/revocation_notifier.py
|
||||||
|
+++ b/keylime/revocation_notifier.py
|
||||||
|
@@ -165,6 +165,29 @@ def notify_webhook(tosend):
|
||||||
|
cert_key = None
|
||||||
|
|
||||||
|
|
||||||
|
+def process_revocation(revocation, callback, cert_path):
|
||||||
|
+ global cert_key
|
||||||
|
+
|
||||||
|
+ if cert_key is None:
|
||||||
|
+ # load up the CV signing public key
|
||||||
|
+ if cert_path is not None and os.path.exists(cert_path):
|
||||||
|
+ logger.info("Lazy loading the revocation certificate from %s", cert_path)
|
||||||
|
+ with open(cert_path, "rb") as f:
|
||||||
|
+ certpem = f.read()
|
||||||
|
+ 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)
|
||||||
|
+ 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")):
|
||||||
|
+ logger.error("Invalid revocation message siganture %s", revocation)
|
||||||
|
+ else:
|
||||||
|
+ message = json.loads(revocation["msg"])
|
||||||
|
+ logger.debug("Revocation signature validated for revocation: %s", message)
|
||||||
|
+ callback(message)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def await_notifications(callback, revocation_cert_path):
|
||||||
|
# keep old typo "listen_notfications" around for a few versions
|
||||||
|
assert config.getboolean("cloud_agent", "listen_notifications", fallback=False) or config.getboolean(
|
||||||
|
@@ -175,8 +198,6 @@ def await_notifications(callback, revocation_cert_path):
|
||||||
|
except ImportError as error:
|
||||||
|
raise Exception("install PyZMQ for 'listen_notifications' option") from error
|
||||||
|
|
||||||
|
- global cert_key
|
||||||
|
-
|
||||||
|
if revocation_cert_path is None:
|
||||||
|
raise Exception("must specify revocation_cert_path")
|
||||||
|
|
||||||
|
@@ -197,25 +218,7 @@ def await_notifications(callback, revocation_cert_path):
|
||||||
|
while True:
|
||||||
|
rawbody = mysock.recv()
|
||||||
|
body = json.loads(rawbody)
|
||||||
|
-
|
||||||
|
- if cert_key is None:
|
||||||
|
- # load up the CV signing public key
|
||||||
|
- if revocation_cert_path is not None and os.path.exists(revocation_cert_path):
|
||||||
|
- logger.info("Lazy loading the revocation certificate from %s", revocation_cert_path)
|
||||||
|
- with open(revocation_cert_path, "rb") as f:
|
||||||
|
- certpem = f.read()
|
||||||
|
- cert_key = crypto.x509_import_pubkey(certpem)
|
||||||
|
-
|
||||||
|
- if cert_key is None:
|
||||||
|
- logger.warning("Unable to check signature of revocation message: %s not available", revocation_cert_path)
|
||||||
|
- elif "signature" not in body or body["signature"] == "none":
|
||||||
|
- logger.warning("No signature on revocation message from server")
|
||||||
|
- elif not crypto.rsa_verify(cert_key, body["msg"].encode("utf-8"), body["signature"].encode("utf-8")):
|
||||||
|
- logger.error("Invalid revocation message siganture %s", body)
|
||||||
|
- else:
|
||||||
|
- message = json.loads(body["msg"])
|
||||||
|
- logger.debug("Revocation signature validated for revocation: %s", message)
|
||||||
|
- callback(message)
|
||||||
|
+ process_revocation(body, callback, revocation_cert_path)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
--
|
||||||
|
2.35.1
|
||||||
|
|
175
0003-keylime_agent-Support-notifications-revocation-REST-.patch
Normal file
175
0003-keylime_agent-Support-notifications-revocation-REST-.patch
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
From 3defa4f6c399a4965dbea8b8188992bb470c2215 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Daiki Ueno <dueno@redhat.com>
|
||||||
|
Date: Mon, 21 Mar 2022 11:46:08 +0100
|
||||||
|
Subject: [PATCH 3/5] keylime_agent: Support /notifications/revocation REST
|
||||||
|
method
|
||||||
|
|
||||||
|
Signed-off-by: Daiki Ueno <dueno@redhat.com>
|
||||||
|
---
|
||||||
|
keylime/keylime_agent.py | 109 ++++++++++++++++++++++++---------------
|
||||||
|
1 file changed, 68 insertions(+), 41 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/keylime/keylime_agent.py b/keylime/keylime_agent.py
|
||||||
|
index f8ea0f5..9ee9797 100644
|
||||||
|
--- a/keylime/keylime_agent.py
|
||||||
|
+++ b/keylime/keylime_agent.py
|
||||||
|
@@ -210,17 +210,13 @@ class Handler(BaseHTTPRequestHandler):
|
||||||
|
rest_params = web_util.get_restful_params(self.path)
|
||||||
|
|
||||||
|
if rest_params is None:
|
||||||
|
- web_util.echo_json_response(self, 405, "Not Implemented: Use /keys/ interface")
|
||||||
|
+ web_util.echo_json_response(self, 405, "Not Implemented: Use /keys/ or /notifications/ interface")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not rest_params["api_version"]:
|
||||||
|
web_util.echo_json_response(self, 400, "API Version not supported")
|
||||||
|
return
|
||||||
|
|
||||||
|
- if rest_params.get("keys", None) not in ["ukey", "vkey"]:
|
||||||
|
- web_util.echo_json_response(self, 400, "Only /keys/ukey or /keys/vkey are supported")
|
||||||
|
- return
|
||||||
|
-
|
||||||
|
content_length = int(self.headers.get("Content-Length", 0))
|
||||||
|
if content_length <= 0:
|
||||||
|
logger.warning("POST returning 400 response, expected content in message. url: %s", self.path)
|
||||||
|
@@ -230,6 +226,26 @@ class Handler(BaseHTTPRequestHandler):
|
||||||
|
post_body = self.rfile.read(content_length)
|
||||||
|
try:
|
||||||
|
json_body = json.loads(post_body)
|
||||||
|
+ except Exception as e:
|
||||||
|
+ logger.warning("POST returning 400 response, could not parse body data: %s", e)
|
||||||
|
+ web_util.echo_json_response(self, 400, "content is invalid")
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if "notifications" in rest_params:
|
||||||
|
+ if rest_params["notifications"] == "revocation":
|
||||||
|
+ revocation_notifier.process_revocation(
|
||||||
|
+ json_body, perform_actions, cert_path=self.server.revocation_cert_path
|
||||||
|
+ )
|
||||||
|
+ web_util.echo_json_response(self, 200, "Success")
|
||||||
|
+ else:
|
||||||
|
+ web_util.echo_json_response(self, 400, "Only /notifications/revocation is supported")
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if rest_params.get("keys", None) not in ["ukey", "vkey"]:
|
||||||
|
+ web_util.echo_json_response(self, 400, "Only /keys/ukey or /keys/vkey are supported")
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
b64_encrypted_key = json_body["encrypted_key"]
|
||||||
|
decrypted_key = crypto.rsa_decrypt(self.server.rsaprivatekey, base64.b64decode(b64_encrypted_key))
|
||||||
|
except (ValueError, KeyError, TypeError) as e:
|
||||||
|
@@ -392,6 +408,7 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
|
rsakey_path = None
|
||||||
|
mtls_cert_enabled = False
|
||||||
|
mtls_cert = None
|
||||||
|
+ revocation_cert_path = None
|
||||||
|
done = threading.Event()
|
||||||
|
auth_tag = None
|
||||||
|
payload = None
|
||||||
|
@@ -456,6 +473,13 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
|
self.mtls_cert = None
|
||||||
|
logger.info("WARNING: mTLS disabled, Tenant and Verifier will reach out to agent via HTTP")
|
||||||
|
|
||||||
|
+ self.revocation_cert_path = config.get("cloud_agent", "revocation_cert")
|
||||||
|
+ if self.revocation_cert_path == "default":
|
||||||
|
+ self.revocation_cert_path = os.path.join(secdir, "unzipped/RevocationNotifier-cert.crt")
|
||||||
|
+ elif self.revocation_cert_path[0] != "/":
|
||||||
|
+ # if it is a relative, convert to absolute in work_dir
|
||||||
|
+ self.revocation_cert_path = os.path.abspath(os.path.join(config.WORK_DIR, self.revocation_cert_path))
|
||||||
|
+
|
||||||
|
# attempt to get a U value from the TPM NVRAM
|
||||||
|
nvram_u = tpm_instance.read_key_nvram()
|
||||||
|
if nvram_u is not None:
|
||||||
|
@@ -549,6 +573,45 @@ class CloudAgentHTTPServer(ThreadingMixIn, HTTPServer):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
+# Execute revocation action
|
||||||
|
+def perform_actions(revocation):
|
||||||
|
+ actionlist = []
|
||||||
|
+
|
||||||
|
+ # load the actions from inside the keylime module
|
||||||
|
+ actionlisttxt = config.get("cloud_agent", "revocation_actions")
|
||||||
|
+ if actionlisttxt.strip() != "":
|
||||||
|
+ actionlist = actionlisttxt.split(",")
|
||||||
|
+ actionlist = [f"revocation_actions.{i}" % i for i in actionlist]
|
||||||
|
+
|
||||||
|
+ # load actions from unzipped
|
||||||
|
+ secdir = secure_mount.mount()
|
||||||
|
+ action_list_path = os.path.join(secdir, "unzipped/action_list")
|
||||||
|
+ if os.path.exists(action_list_path):
|
||||||
|
+ with open(action_list_path, encoding="utf-8") as f:
|
||||||
|
+ actionlisttxt = f.read()
|
||||||
|
+ if actionlisttxt.strip() != "":
|
||||||
|
+ localactions = actionlisttxt.strip().split(",")
|
||||||
|
+ for action in localactions:
|
||||||
|
+ if not action.startswith("local_action_"):
|
||||||
|
+ logger.warning("Invalid local action: %s. Must start with local_action_", action)
|
||||||
|
+ else:
|
||||||
|
+ actionlist.append(action)
|
||||||
|
+
|
||||||
|
+ uzpath = os.path.join(secdir, "unzipped")
|
||||||
|
+ if uzpath not in sys.path:
|
||||||
|
+ sys.path.append(uzpath)
|
||||||
|
+
|
||||||
|
+ for action in actionlist:
|
||||||
|
+ logger.info("Executing revocation action %s", action)
|
||||||
|
+ try:
|
||||||
|
+ module = importlib.import_module(action)
|
||||||
|
+ execute = getattr(module, "execute")
|
||||||
|
+ loop = asyncio.new_event_loop()
|
||||||
|
+ loop.run_until_complete(execute(revocation))
|
||||||
|
+ except Exception as e:
|
||||||
|
+ logger.warning("Exception during execution of revocation action %s: %s", action, e)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def revocation_listener():
|
||||||
|
"""
|
||||||
|
This configures and starts the revocation listener. It is designed to be started in a separate process.
|
||||||
|
@@ -573,42 +636,6 @@ def revocation_listener():
|
||||||
|
# if it is a relative, convert to absolute in work_dir
|
||||||
|
cert_path = os.path.abspath(os.path.join(config.WORK_DIR, cert_path))
|
||||||
|
|
||||||
|
- # Callback function handling the revocations
|
||||||
|
- def perform_actions(revocation):
|
||||||
|
- actionlist = []
|
||||||
|
-
|
||||||
|
- # load the actions from inside the keylime module
|
||||||
|
- actionlisttxt = config.get("cloud_agent", "revocation_actions")
|
||||||
|
- if actionlisttxt.strip() != "":
|
||||||
|
- actionlist = actionlisttxt.split(",")
|
||||||
|
- actionlist = [f"revocation_actions.{i}" % i for i in actionlist]
|
||||||
|
-
|
||||||
|
- # load actions from unzipped
|
||||||
|
- action_list_path = os.path.join(secdir, "unzipped/action_list")
|
||||||
|
- if os.path.exists(action_list_path):
|
||||||
|
- with open(action_list_path, encoding="utf-8") as f:
|
||||||
|
- actionlisttxt = f.read()
|
||||||
|
- if actionlisttxt.strip() != "":
|
||||||
|
- localactions = actionlisttxt.strip().split(",")
|
||||||
|
- for action in localactions:
|
||||||
|
- if not action.startswith("local_action_"):
|
||||||
|
- logger.warning("Invalid local action: %s. Must start with local_action_", action)
|
||||||
|
- else:
|
||||||
|
- actionlist.append(action)
|
||||||
|
-
|
||||||
|
- uzpath = os.path.join(secdir, "unzipped")
|
||||||
|
- if uzpath not in sys.path:
|
||||||
|
- sys.path.append(uzpath)
|
||||||
|
-
|
||||||
|
- for action in actionlist:
|
||||||
|
- logger.info("Executing revocation action %s", action)
|
||||||
|
- try:
|
||||||
|
- module = importlib.import_module(action)
|
||||||
|
- execute = getattr(module, "execute")
|
||||||
|
- asyncio.get_event_loop().run_until_complete(execute(revocation))
|
||||||
|
- except Exception as e:
|
||||||
|
- logger.warning("Exception during execution of revocation action %s: %s", action, e)
|
||||||
|
-
|
||||||
|
try:
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
--
|
||||||
|
2.35.1
|
||||||
|
|
356
0004-cloud_verifier-Support-notifications-revocation-REST.patch
Normal file
356
0004-cloud_verifier-Support-notifications-revocation-REST.patch
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
From 16d3a31145e3d0001e6c6621adb6dbaf831cb03f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Daiki Ueno <dueno@redhat.com>
|
||||||
|
Date: Mon, 21 Mar 2022 13:08:29 +0100
|
||||||
|
Subject: [PATCH 4/5] cloud_verifier: Support /notifications/revocation REST
|
||||||
|
API
|
||||||
|
|
||||||
|
Signed-off-by: Daiki Ueno <dueno@redhat.com>
|
||||||
|
---
|
||||||
|
keylime.conf | 22 ++++--
|
||||||
|
keylime/cloud_verifier_common.py | 16 +----
|
||||||
|
keylime/cloud_verifier_tornado.py | 108 +++++++++++++++++++++++-------
|
||||||
|
keylime/revocation_notifier.py | 18 ++++-
|
||||||
|
4 files changed, 119 insertions(+), 45 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/keylime.conf b/keylime.conf
|
||||||
|
index fbd1119..c393e22 100644
|
||||||
|
--- a/keylime.conf
|
||||||
|
+++ b/keylime.conf
|
||||||
|
@@ -269,9 +269,21 @@ max_retries = 5
|
||||||
|
# will done as fast as possible. Floating point values accepted here.
|
||||||
|
quote_interval = 2
|
||||||
|
|
||||||
|
-# Whether to turn on the zero mq based revocation notifier system.
|
||||||
|
-# Currently this only works if you are using keylime-CA.
|
||||||
|
-revocation_notifier = True
|
||||||
|
+# Enable listed revocation notification methods.
|
||||||
|
+#
|
||||||
|
+# Available methods are:
|
||||||
|
+#
|
||||||
|
+# "zeromq": Enable the ZeroMQ based revocation notification method;
|
||||||
|
+# revocation_notifier_ip and revocation_notifier_port options must be
|
||||||
|
+# set. Currently this only works if you are using keylime-CA.
|
||||||
|
+#
|
||||||
|
+# "webhook": Send notification via webhook. The endpoint URL must be
|
||||||
|
+# configured with webhook_url option. This can be used to notify other
|
||||||
|
+# systems that do not have a Keylime agent running.
|
||||||
|
+#
|
||||||
|
+# "agent": Deliver notification directly to the agent via the REST
|
||||||
|
+# protocol.
|
||||||
|
+revocation_notifiers = zeromq
|
||||||
|
|
||||||
|
# The binding address and port of the revocation notifier service.
|
||||||
|
# If the 'revocation_notifier' option is set to "true", then the verifier
|
||||||
|
@@ -279,10 +291,6 @@ revocation_notifier = True
|
||||||
|
revocation_notifier_ip = 127.0.0.1
|
||||||
|
revocation_notifier_port = 8992
|
||||||
|
|
||||||
|
-# Enable revocation notifications via webhook. This can be used to notify other
|
||||||
|
-# systems that do not have a Keylime agent running.
|
||||||
|
-revocation_notifier_webhook = False
|
||||||
|
-
|
||||||
|
# Webhook url for revocation notifications.
|
||||||
|
webhook_url = ''
|
||||||
|
|
||||||
|
diff --git a/keylime/cloud_verifier_common.py b/keylime/cloud_verifier_common.py
|
||||||
|
index 52d6908..ab00768 100644
|
||||||
|
--- a/keylime/cloud_verifier_common.py
|
||||||
|
+++ b/keylime/cloud_verifier_common.py
|
||||||
|
@@ -7,7 +7,7 @@ import ast
|
||||||
|
import base64
|
||||||
|
import time
|
||||||
|
|
||||||
|
-from keylime import config, crypto, json, keylime_logging, revocation_notifier
|
||||||
|
+from keylime import config, crypto, json, keylime_logging
|
||||||
|
from keylime.agentstates import AgentAttestStates
|
||||||
|
from keylime.common import algorithms, validators
|
||||||
|
from keylime.failure import Component, Failure
|
||||||
|
@@ -268,14 +268,7 @@ def process_get_status(agent):
|
||||||
|
|
||||||
|
|
||||||
|
# sign a message with revocation key. telling of verification problem
|
||||||
|
-
|
||||||
|
-
|
||||||
|
-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", fallback=False)
|
||||||
|
- if not (send_mq or send_webhook):
|
||||||
|
- return
|
||||||
|
-
|
||||||
|
+def prepare_error(agent, msgtype="revocation", event=None):
|
||||||
|
# prepare the revocation message:
|
||||||
|
revocation = {
|
||||||
|
"type": msgtype,
|
||||||
|
@@ -300,10 +293,7 @@ def notify_error(agent, msgtype="revocation", event=None):
|
||||||
|
|
||||||
|
else:
|
||||||
|
tosend["signature"] = "none"
|
||||||
|
- if send_mq:
|
||||||
|
- revocation_notifier.notify(tosend)
|
||||||
|
- if send_webhook:
|
||||||
|
- revocation_notifier.notify_webhook(tosend)
|
||||||
|
+ return tosend
|
||||||
|
|
||||||
|
|
||||||
|
def validate_agent_data(agent_data):
|
||||||
|
diff --git a/keylime/cloud_verifier_tornado.py b/keylime/cloud_verifier_tornado.py
|
||||||
|
index 60abf37..a8c08d2 100644
|
||||||
|
--- a/keylime/cloud_verifier_tornado.py
|
||||||
|
+++ b/keylime/cloud_verifier_tornado.py
|
||||||
|
@@ -9,6 +9,7 @@ import os
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
+from concurrent.futures import ThreadPoolExecutor
|
||||||
|
from multiprocessing import Process
|
||||||
|
|
||||||
|
import tornado.ioloop
|
||||||
|
@@ -35,6 +36,9 @@ from keylime.failure import MAX_SEVERITY_LABEL, Component, Failure
|
||||||
|
|
||||||
|
logger = keylime_logging.init_logging("cloudverifier")
|
||||||
|
|
||||||
|
+# mTLS configuration to connect to the agent
|
||||||
|
+mtls_options = None
|
||||||
|
+
|
||||||
|
|
||||||
|
try:
|
||||||
|
engine = DBEngineManager().make_engine("cloud_verifier")
|
||||||
|
@@ -232,11 +236,6 @@ class VersionHandler(BaseHandler):
|
||||||
|
|
||||||
|
|
||||||
|
class AgentsHandler(BaseHandler):
|
||||||
|
- mtls_options = None # Stores the cert, key and password used by the verifier for mTLS connections
|
||||||
|
-
|
||||||
|
- def initialize(self, mtls_options):
|
||||||
|
- self.mtls_options = mtls_options
|
||||||
|
-
|
||||||
|
def head(self):
|
||||||
|
"""HEAD not supported"""
|
||||||
|
web_util.echo_json_response(self, 405, "HEAD not supported")
|
||||||
|
@@ -496,9 +495,7 @@ class AgentsHandler(BaseHandler):
|
||||||
|
mtls_cert = agent_data["mtls_cert"]
|
||||||
|
agent_data["ssl_context"] = None
|
||||||
|
if agent_mtls_cert_enabled and mtls_cert:
|
||||||
|
- agent_data["ssl_context"] = web_util.generate_agent_mtls_context(
|
||||||
|
- mtls_cert, self.mtls_options
|
||||||
|
- )
|
||||||
|
+ agent_data["ssl_context"] = web_util.generate_agent_mtls_context(mtls_cert, mtls_options)
|
||||||
|
|
||||||
|
if agent_data["ssl_context"] is None:
|
||||||
|
logger.warning("Connecting to agent without mTLS: %s", agent_id)
|
||||||
|
@@ -566,7 +563,7 @@ class AgentsHandler(BaseHandler):
|
||||||
|
if not isinstance(agent, dict):
|
||||||
|
agent = _from_db_obj(agent)
|
||||||
|
if agent["mtls_cert"]:
|
||||||
|
- agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], self.mtls_options)
|
||||||
|
+ agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], mtls_options)
|
||||||
|
agent["operational_state"] = states.START
|
||||||
|
asyncio.ensure_future(process_agent(agent, states.GET_QUOTE))
|
||||||
|
web_util.echo_json_response(self, 200, "Success")
|
||||||
|
@@ -860,6 +857,70 @@ async def invoke_provide_v(agent):
|
||||||
|
asyncio.ensure_future(process_agent(agent, states.GET_QUOTE))
|
||||||
|
|
||||||
|
|
||||||
|
+async def invoke_notify_error(agent, tosend):
|
||||||
|
+ if agent is None:
|
||||||
|
+ logger.warning("Agent deleted while being processed")
|
||||||
|
+ return
|
||||||
|
+ kwargs = {
|
||||||
|
+ "data": tosend,
|
||||||
|
+ }
|
||||||
|
+ if agent["ssl_context"]:
|
||||||
|
+ kwargs["context"] = agent["ssl_context"]
|
||||||
|
+ res = tornado_requests.request(
|
||||||
|
+ "POST",
|
||||||
|
+ f"http://{agent['ip']}:{agent['port']}/v{agent['supported_version']}/notifications/revocation",
|
||||||
|
+ **kwargs,
|
||||||
|
+ )
|
||||||
|
+ response = await res
|
||||||
|
+
|
||||||
|
+ if response is None:
|
||||||
|
+ logger.warning(
|
||||||
|
+ "Empty Notify Revocation response from cloud agent %s",
|
||||||
|
+ agent["agent_id"],
|
||||||
|
+ )
|
||||||
|
+ elif response.status_code != 200:
|
||||||
|
+ logger.warning(
|
||||||
|
+ "Unexpected Notify Revocation response error for cloud agent %s, Error: %s",
|
||||||
|
+ agent["agent_id"],
|
||||||
|
+ response.status_code,
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+async def notify_error(agent, msgtype="revocation", event=None):
|
||||||
|
+ notifiers = revocation_notifier.get_notifiers()
|
||||||
|
+ if len(notifiers) == 0:
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ tosend = cloud_verifier_common.prepare_error(agent, msgtype, event)
|
||||||
|
+ if "webhook" in notifiers:
|
||||||
|
+ revocation_notifier.notify_webhook(tosend)
|
||||||
|
+ if "zeromq" in notifiers:
|
||||||
|
+ revocation_notifier.notify(tosend)
|
||||||
|
+ if "agent" in notifiers:
|
||||||
|
+ verifier_id = config.get(
|
||||||
|
+ "cloud_verifier", "cloudverifier_id", fallback=cloud_verifier_common.DEFAULT_VERIFIER_ID
|
||||||
|
+ )
|
||||||
|
+ session = get_session()
|
||||||
|
+ agents = session.query(VerfierMain).filter_by(verifier_id=verifier_id).all()
|
||||||
|
+ futures = []
|
||||||
|
+ loop = asyncio.get_event_loop()
|
||||||
|
+ # Notify all agents asynchronously through a thread pool
|
||||||
|
+ with ThreadPoolExecutor() as pool:
|
||||||
|
+ for agent_db_obj in agents:
|
||||||
|
+ if agent_db_obj.agent_id != agent["agent_id"]:
|
||||||
|
+ agent = _from_db_obj(agent_db_obj)
|
||||||
|
+ if agent["mtls_cert"]:
|
||||||
|
+ agent["ssl_context"] = web_util.generate_agent_mtls_context(agent["mtls_cert"], mtls_options)
|
||||||
|
+ func = functools.partial(invoke_notify_error, agent, tosend)
|
||||||
|
+ futures.append(await loop.run_in_executor(pool, func))
|
||||||
|
+ # Wait for all tasks complete in 60 seconds
|
||||||
|
+ try:
|
||||||
|
+ for f in asyncio.as_completed(futures, timeout=60):
|
||||||
|
+ await f
|
||||||
|
+ except asyncio.TimeoutError as e:
|
||||||
|
+ logger.error("Timeout during notifying error to agents: %s", e)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
async def process_agent(agent, new_operational_state, failure=Failure(Component.INTERNAL, ["verifier"])):
|
||||||
|
# Convert to dict if the agent arg is a db object
|
||||||
|
if not isinstance(agent, dict):
|
||||||
|
@@ -900,7 +961,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component.
|
||||||
|
|
||||||
|
# issue notification for invalid quotes
|
||||||
|
if new_operational_state == states.INVALID_QUOTE:
|
||||||
|
- cloud_verifier_common.notify_error(agent, event=failure.highest_severity_event)
|
||||||
|
+ await notify_error(agent, event=failure.highest_severity_event)
|
||||||
|
|
||||||
|
# When the failure is irrecoverable we stop polling the agent
|
||||||
|
if not failure.recoverable or failure.highest_severity == MAX_SEVERITY_LABEL:
|
||||||
|
@@ -975,9 +1036,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component.
|
||||||
|
)
|
||||||
|
failure.add_event("not_reachable", "agent was not reachable from verifier", False)
|
||||||
|
if agent["first_verified"]: # only notify on previously good agents
|
||||||
|
- cloud_verifier_common.notify_error(
|
||||||
|
- agent, msgtype="comm_error", event=failure.highest_severity_event
|
||||||
|
- )
|
||||||
|
+ await notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event)
|
||||||
|
else:
|
||||||
|
logger.debug("Communication error for new agent. No notification will be sent")
|
||||||
|
await process_agent(agent, states.FAILED, failure)
|
||||||
|
@@ -1004,7 +1063,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component.
|
||||||
|
maxr,
|
||||||
|
)
|
||||||
|
failure.add_event("not_reachable_v", "agent was not reachable to provide V", False)
|
||||||
|
- cloud_verifier_common.notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event)
|
||||||
|
+ await notify_error(agent, msgtype="comm_error", event=failure.highest_severity_event)
|
||||||
|
await process_agent(agent, states.FAILED, failure)
|
||||||
|
else:
|
||||||
|
agent["operational_state"] = states.PROVIDE_V
|
||||||
|
@@ -1031,7 +1090,7 @@ async def process_agent(agent, new_operational_state, failure=Failure(Component.
|
||||||
|
await process_agent(agent, states.FAILED, failure)
|
||||||
|
|
||||||
|
|
||||||
|
-async def activate_agents(verifier_id, verifier_ip, verifier_port, mtls_options):
|
||||||
|
+async def activate_agents(verifier_id, verifier_ip, verifier_port):
|
||||||
|
session = get_session()
|
||||||
|
aas = get_AgentAttestStates()
|
||||||
|
try:
|
||||||
|
@@ -1100,7 +1159,7 @@ def main():
|
||||||
|
# print out API versions we support
|
||||||
|
keylime_api_version.log_api_versions(logger)
|
||||||
|
|
||||||
|
- context, mtls_options = web_util.init_mtls(logger=logger)
|
||||||
|
+ context, server_mtls_options = web_util.init_mtls(logger=logger)
|
||||||
|
|
||||||
|
# Check for user defined CA to connect to agent
|
||||||
|
agent_mtls_cert = config.get("cloud_verifier", "agent_mtls_cert", fallback=None)
|
||||||
|
@@ -1108,12 +1167,15 @@ def main():
|
||||||
|
agent_mtls_private_key_pw = config.get("cloud_verifier", "agent_mtls_private_key_pw", fallback=None)
|
||||||
|
|
||||||
|
# Only set custom options if the cert should not be the same as used by the verifier
|
||||||
|
- if agent_mtls_cert != "CV":
|
||||||
|
+ global mtls_options
|
||||||
|
+ if agent_mtls_cert == "CV":
|
||||||
|
+ mtls_options = server_mtls_options
|
||||||
|
+ else:
|
||||||
|
mtls_options = (agent_mtls_cert, agent_mtls_private_key, agent_mtls_private_key_pw)
|
||||||
|
|
||||||
|
app = tornado.web.Application(
|
||||||
|
[
|
||||||
|
- (r"/v?[0-9]+(?:\.[0-9]+)?/agents/.*", AgentsHandler, {"mtls_options": mtls_options}),
|
||||||
|
+ (r"/v?[0-9]+(?:\.[0-9]+)?/agents/.*", AgentsHandler),
|
||||||
|
(r"/v?[0-9]+(?:\.[0-9]+)?/allowlists/.*", AllowlistHandler),
|
||||||
|
(r"/versions?", VersionHandler),
|
||||||
|
(r".*", MainHandler),
|
||||||
|
@@ -1149,17 +1211,17 @@ def main():
|
||||||
|
server.start()
|
||||||
|
if task_id == 0:
|
||||||
|
# Reactivate agents
|
||||||
|
- asyncio.ensure_future(
|
||||||
|
- activate_agents(cloudverifier_id, cloudverifier_host, cloudverifier_port, mtls_options)
|
||||||
|
- )
|
||||||
|
+ asyncio.ensure_future(activate_agents(cloudverifier_id, cloudverifier_host, cloudverifier_port))
|
||||||
|
tornado.ioloop.IOLoop.current().start()
|
||||||
|
logger.debug("Server %s stopped.", task_id)
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
processes = []
|
||||||
|
|
||||||
|
+ run_revocation_notifier = "zeromq" in revocation_notifier.get_notifiers()
|
||||||
|
+
|
||||||
|
def sig_handler(*_):
|
||||||
|
- if config.getboolean("cloud_verifier", "revocation_notifier"):
|
||||||
|
+ if run_revocation_notifier:
|
||||||
|
revocation_notifier.stop_broker()
|
||||||
|
for p in processes:
|
||||||
|
p.join()
|
||||||
|
@@ -1167,7 +1229,7 @@ def main():
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, sig_handler)
|
||||||
|
signal.signal(signal.SIGTERM, sig_handler)
|
||||||
|
- if config.getboolean("cloud_verifier", "revocation_notifier"):
|
||||||
|
+ if run_revocation_notifier:
|
||||||
|
logger.info(
|
||||||
|
"Starting service for revocation notifications on port %s",
|
||||||
|
config.getint("cloud_verifier", "revocation_notifier_port"),
|
||||||
|
diff --git a/keylime/revocation_notifier.py b/keylime/revocation_notifier.py
|
||||||
|
index 7cfe0e5..0628a64 100644
|
||||||
|
--- a/keylime/revocation_notifier.py
|
||||||
|
+++ b/keylime/revocation_notifier.py
|
||||||
|
@@ -22,8 +22,22 @@ broker_proc: Optional[Process] = None
|
||||||
|
_SOCKET_PATH = "/var/run/keylime/keylime.verifier.ipc"
|
||||||
|
|
||||||
|
|
||||||
|
+# return the revocation notification methods for cloud verifier
|
||||||
|
+def get_notifiers():
|
||||||
|
+ notifiers = set(config.get("cloud_verifier", "revocation_notifiers", fallback="").split(","))
|
||||||
|
+ if ("zeromq" not in notifiers) and config.getboolean("cloud_verifier", "revocation_notifier", fallback=False):
|
||||||
|
+ logger.warning("Warning: 'revocation_notifier' option is deprecated; use 'revocation_notifiers'")
|
||||||
|
+ notifiers.add("zeromq")
|
||||||
|
+ if ("webhook" not in notifiers) and config.getboolean(
|
||||||
|
+ "cloud_verifier", "revocation_notifier_webhook", fallback=False
|
||||||
|
+ ):
|
||||||
|
+ logger.warning("Warning: 'revocation_notifier_webhook' option is deprecated; use 'revocation_notifiers'")
|
||||||
|
+ notifiers.add("webhook")
|
||||||
|
+ return notifiers.intersection({"zeromq", "webhook", "agent"})
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def start_broker():
|
||||||
|
- assert config.getboolean("cloud_verifier", "revocation_notifier")
|
||||||
|
+ assert "zeromq" in get_notifiers()
|
||||||
|
try:
|
||||||
|
import zmq # pylint: disable=import-outside-toplevel
|
||||||
|
except ImportError as error:
|
||||||
|
@@ -78,7 +92,7 @@ def stop_broker():
|
||||||
|
|
||||||
|
|
||||||
|
def notify(tosend):
|
||||||
|
- assert config.getboolean("cloud_verifier", "revocation_notifier")
|
||||||
|
+ assert "zeromq" in get_notifiers()
|
||||||
|
try:
|
||||||
|
import zmq # pylint: disable=import-outside-toplevel
|
||||||
|
except ImportError as error:
|
||||||
|
--
|
||||||
|
2.35.1
|
||||||
|
|
117
0005-Use-python3-gpg-instead-of-python3-gnupg.patch
Normal file
117
0005-Use-python3-gpg-instead-of-python3-gnupg.patch
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
From 49bc0a3afbbe3740bb857b530440364b021a865f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Sergio Correia <scorreia@redhat.com>
|
||||||
|
Date: Fri, 17 Jun 2022 19:57:17 -0300
|
||||||
|
Subject: [PATCH 5/5] Use python3-gpg instead of python3-gnupg
|
||||||
|
|
||||||
|
The former uses GPGME and is the recommended way of using GnuPG from
|
||||||
|
applications, by the GnuPG initiative, as it provides a better
|
||||||
|
documented API [1][2].
|
||||||
|
|
||||||
|
python-gpg is also already present in some distros, e.g. Fedora and
|
||||||
|
CentOS Stream, as it is a dependency of their package manager (dnf).
|
||||||
|
|
||||||
|
It is also available in other major distros such as Debian, Ubuntu,
|
||||||
|
OpenSUSE, so no disruptions are to be expected regarding packaging.
|
||||||
|
|
||||||
|
[1] https://gnupg.org/software/gpgme/index.html
|
||||||
|
[2] https://wiki.python.org/moin/GnuPrivacyGuard
|
||||||
|
---
|
||||||
|
installer.sh | 6 +++---
|
||||||
|
keylime/signing.py | 27 +++++++++++++--------------
|
||||||
|
requirements.txt | 3 +--
|
||||||
|
3 files changed, 17 insertions(+), 19 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/installer.sh b/installer.sh
|
||||||
|
index 6618e1d..4355b33 100755
|
||||||
|
--- a/installer.sh
|
||||||
|
+++ b/installer.sh
|
||||||
|
@@ -58,7 +58,7 @@ case "$ID" in
|
||||||
|
echo "${ID} selected."
|
||||||
|
PACKAGE_MGR=$(command -v apt-get)
|
||||||
|
PYTHON_PREIN="git patch"
|
||||||
|
- PYTHON_DEPS="python3 python3-pip python3-dev python3-setuptools python3-zmq python3-tornado python3-cryptography python3-requests python3-psutil gcc g++ libssl-dev swig python3-yaml python3-gnupg python3-lark wget"
|
||||||
|
+ PYTHON_DEPS="python3 python3-pip python3-dev python3-setuptools python3-zmq python3-tornado python3-cryptography python3-requests python3-psutil gcc g++ libssl-dev swig python3-yaml python3-gpg python3-lark wget"
|
||||||
|
if [ "$(uname -m)" = "x86_64" ]; then
|
||||||
|
PYTHON_DEPS+=" libefivar-dev"
|
||||||
|
fi
|
||||||
|
@@ -96,7 +96,7 @@ case "$ID" in
|
||||||
|
PACKAGE_MGR=$(command -v dnf)
|
||||||
|
NEED_EPEL=1
|
||||||
|
PYTHON_PREIN="python3 python3-devel python3-setuptools python3-pip"
|
||||||
|
- PYTHON_DEPS="gcc gcc-c++ openssl-devel python3-yaml python3-requests swig python3-cryptography wget git python3-tornado python3-zmq python3-gnupg python3-psutil"
|
||||||
|
+ PYTHON_DEPS="gcc gcc-c++ openssl-devel python3-yaml python3-requests swig python3-cryptography wget git python3-tornado python3-zmq python3-gpg python3-psutil"
|
||||||
|
if [ "$(uname -m)" = "x86_64" ]; then
|
||||||
|
PYTHON_DEPS+=" efivar-libs"
|
||||||
|
fi
|
||||||
|
@@ -116,7 +116,7 @@ case "$ID" in
|
||||||
|
echo "${ID} selected."
|
||||||
|
PACKAGE_MGR=$(command -v dnf)
|
||||||
|
PYTHON_PREIN="python3 python3-devel python3-setuptools git wget patch"
|
||||||
|
- PYTHON_DEPS="python3-pip gcc gcc-c++ openssl-devel swig python3-pyyaml python3-zmq python3-cryptography python3-tornado python3-requests python3-gnupg yaml-cpp-devel procps-ng python3-psutil python3-lark-parser"
|
||||||
|
+ PYTHON_DEPS="python3-pip gcc gcc-c++ openssl-devel swig python3-pyyaml python3-zmq python3-cryptography python3-tornado python3-requests python3-gpg yaml-cpp-devel procps-ng python3-psutil python3-lark-parser"
|
||||||
|
if [ "$(uname -m)" = "x86_64" ]; then
|
||||||
|
PYTHON_DEPS+=" efivar-devel"
|
||||||
|
fi
|
||||||
|
diff --git a/keylime/signing.py b/keylime/signing.py
|
||||||
|
index 1353c1e..a1be9c7 100644
|
||||||
|
--- a/keylime/signing.py
|
||||||
|
+++ b/keylime/signing.py
|
||||||
|
@@ -5,7 +5,7 @@ Copyright 2017 Massachusetts Institute of Technology.
|
||||||
|
|
||||||
|
import tempfile
|
||||||
|
|
||||||
|
-import gnupg
|
||||||
|
+import gpg
|
||||||
|
from cryptography.exceptions import InvalidSignature
|
||||||
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
|
@@ -55,19 +55,18 @@ def verify_signature(key, sig, file):
|
||||||
|
|
||||||
|
# PGP
|
||||||
|
if key_header == "-----BEGIN PGP PUBLIC KEY BLOCK-----":
|
||||||
|
- gpg = gnupg.GPG()
|
||||||
|
- logger.debug("Importing GPG key")
|
||||||
|
- gpg_imported = gpg.import_keys(key.decode("utf-8"))
|
||||||
|
- if gpg_imported.count == 1: # pylint: disable=E1101
|
||||||
|
- logger.debug("GPG key successfully imported")
|
||||||
|
- else:
|
||||||
|
- raise Exception("Unable to import GPG key")
|
||||||
|
-
|
||||||
|
- # The Python PGP library won't let you read a signature from memory, hence this hack.
|
||||||
|
- with tempfile.NamedTemporaryFile() as temp_sig:
|
||||||
|
- temp_sig.write(sig)
|
||||||
|
- temp_sig.flush()
|
||||||
|
- verified = gpg.verify_data(temp_sig.name, file)
|
||||||
|
+ verified = False
|
||||||
|
+ with tempfile.TemporaryDirectory() as gpg_homedir:
|
||||||
|
+ ctx = gpg.Context(home_dir=gpg_homedir)
|
||||||
|
+ try:
|
||||||
|
+ logger.debug("Importing GPG key")
|
||||||
|
+ result = ctx.key_import(key)
|
||||||
|
+ except Exception as e:
|
||||||
|
+ raise Exception("Unable to import GPG key") from e
|
||||||
|
+
|
||||||
|
+ if result is not None and hasattr(result, "considered") is True:
|
||||||
|
+ _, result = ctx.verify(file, sig)
|
||||||
|
+ verified = result.signatures[0].status == 0
|
||||||
|
|
||||||
|
# OpenSSL
|
||||||
|
elif key_header == "-----BEGIN PUBLIC KEY-----":
|
||||||
|
diff --git a/requirements.txt b/requirements.txt
|
||||||
|
index d31eabc..ca3fac3 100644
|
||||||
|
--- a/requirements.txt
|
||||||
|
+++ b/requirements.txt
|
||||||
|
@@ -10,8 +10,7 @@ pyyaml>=3.11 # MIT
|
||||||
|
requests>=2.6 # Apache-2.0
|
||||||
|
sqlalchemy>=1.3 # MIT
|
||||||
|
alembic>=1.1.0 # MIT
|
||||||
|
-python-gnupg>=0.4.6 # BSD
|
||||||
|
packaging>=20.0 #BSD
|
||||||
|
psutil>=5.4.2 # BSD
|
||||||
|
# Note that lark was renamed from lark-parser with 1.0.0 release
|
||||||
|
-lark>=1.0.0 # MIT
|
||||||
|
\ No newline at end of file
|
||||||
|
+lark>=1.0.0 # MIT
|
||||||
|
--
|
||||||
|
2.35.1
|
||||||
|
|
8
gating.yaml
Normal file
8
gating.yaml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
--- !Policy
|
||||||
|
product_versions:
|
||||||
|
- rhel-9
|
||||||
|
decision_context: osci_compose_gate
|
||||||
|
rules:
|
||||||
|
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.openstack-swtmp.functional}
|
||||||
|
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.beaker-tpm-ima.functional}
|
||||||
|
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.beaker-beaker-swtpm-multihost.functional}
|
244
keylime.spec
Normal file
244
keylime.spec
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
%global srcname keylime
|
||||||
|
|
||||||
|
Name: keylime
|
||||||
|
Version: 6.4.1
|
||||||
|
Release: %{?autorelease}%{!?autorelease:1%{?dist}}
|
||||||
|
Summary: Open source TPM software for Bootstrapping and Maintaining Trust
|
||||||
|
|
||||||
|
BuildArch: noarch
|
||||||
|
|
||||||
|
URL: https://github.com/keylime/keylime
|
||||||
|
Source0: https://github.com/keylime/keylime/archive/refs/tags/v%{version}.tar.gz
|
||||||
|
Source1: %{srcname}.sysusers
|
||||||
|
|
||||||
|
Patch: 0001-Improve-error-handling-when-doing-signature-verifica.patch
|
||||||
|
# Agent-Local revocation.
|
||||||
|
Patch: 0002-revocation_notifier-Factor-out-revocation-message-pr.patch
|
||||||
|
Patch: 0003-keylime_agent-Support-notifications-revocation-REST-.patch
|
||||||
|
Patch: 0004-cloud_verifier-Support-notifications-revocation-REST.patch
|
||||||
|
# python-gpg.
|
||||||
|
Patch: 0005-Use-python3-gpg-instead-of-python3-gnupg.patch
|
||||||
|
|
||||||
|
License: ASL 2.0 and MIT
|
||||||
|
|
||||||
|
BuildRequires: git-core
|
||||||
|
BuildRequires: swig
|
||||||
|
BuildRequires: openssl-devel
|
||||||
|
BuildRequires: python3-devel
|
||||||
|
BuildRequires: python3-dbus
|
||||||
|
BuildRequires: python3-setuptools
|
||||||
|
BuildRequires: systemd-rpm-macros
|
||||||
|
|
||||||
|
Requires: python3-%{srcname} = %{version}-%{release}
|
||||||
|
Requires: %{srcname}-base = %{version}-%{release}
|
||||||
|
Requires: %{srcname}-verifier = %{version}-%{release}
|
||||||
|
Requires: %{srcname}-registrar = %{version}-%{release}
|
||||||
|
Requires: %{srcname}-tenant = %{version}-%{release}
|
||||||
|
|
||||||
|
# Agent.
|
||||||
|
Requires: keylime-agent
|
||||||
|
Suggests: keylime-agent-rust
|
||||||
|
|
||||||
|
%{?python_enable_dependency_generator}
|
||||||
|
%description
|
||||||
|
Keylime is a TPM based highly scalable remote boot attestation
|
||||||
|
and runtime integrity measurement solution.
|
||||||
|
|
||||||
|
%package base
|
||||||
|
Summary: The base package contains the default configuration
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
|
||||||
|
Requires(pre): shadow-utils
|
||||||
|
Requires: efivar-libs
|
||||||
|
Requires: procps-ng
|
||||||
|
Requires: tpm2-tss
|
||||||
|
Requires: tpm2-tools
|
||||||
|
|
||||||
|
|
||||||
|
%description base
|
||||||
|
The base package contains the Keylime default configuration
|
||||||
|
|
||||||
|
%package -n python3-%{srcname}
|
||||||
|
Summary: The Python Keylime module
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
Requires: %{srcname}-base = %{version}-%{release}
|
||||||
|
%{?python_provide:%python_provide python3-%{srcname}}
|
||||||
|
|
||||||
|
Requires: python3-tornado
|
||||||
|
Requires: python3-sqlalchemy
|
||||||
|
Requires: python3-alembic
|
||||||
|
Requires: python3-cryptography
|
||||||
|
Requires: python3-pyyaml
|
||||||
|
Requires: python3-packaging
|
||||||
|
Requires: python3-requests
|
||||||
|
Requires: python3-gpg
|
||||||
|
Requires: python3-lark-parser
|
||||||
|
|
||||||
|
|
||||||
|
%description -n python3-%{srcname}
|
||||||
|
The python3-keylime module implements the functionality used
|
||||||
|
by Keylime components.
|
||||||
|
|
||||||
|
%package verifier
|
||||||
|
Summary: The Python Keylime Verifier component
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
Requires: %{srcname}-base = %{version}-%{release}
|
||||||
|
Requires: python3-%{srcname} = %{version}-%{release}
|
||||||
|
|
||||||
|
%description verifier
|
||||||
|
The Keylime Verifier continuously verifies the integrity state
|
||||||
|
of the machine that the agent is running on.
|
||||||
|
|
||||||
|
%package registrar
|
||||||
|
Summary: The Keylime Registrar component
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
Requires: %{srcname}-base = %{version}-%{release}
|
||||||
|
Requires: python3-%{srcname} = %{version}-%{release}
|
||||||
|
|
||||||
|
%description registrar
|
||||||
|
The Keylime Registrar is a database of all agents registered
|
||||||
|
with Keylime and hosts the public keys of the TPM vendors.
|
||||||
|
|
||||||
|
%package tenant
|
||||||
|
Summary: The Python Keylime Tenant
|
||||||
|
License: MIT
|
||||||
|
|
||||||
|
Requires: %{srcname}-base = %{version}-%{release}
|
||||||
|
Requires: python3-%{srcname} = %{version}-%{release}
|
||||||
|
|
||||||
|
|
||||||
|
%description tenant
|
||||||
|
The Keylime Tenant can be used to provision a Keylime Agent.
|
||||||
|
|
||||||
|
%prep
|
||||||
|
%autosetup -S git -n %{srcname}-%{version}
|
||||||
|
|
||||||
|
%build
|
||||||
|
%py3_build
|
||||||
|
|
||||||
|
%install
|
||||||
|
%py3_install
|
||||||
|
mkdir -p %{buildroot}/%{_sharedstatedir}/%{srcname}
|
||||||
|
mkdir -p --mode=0700 %{buildroot}/%{_rundir}/%{srcname}
|
||||||
|
mkdir -p --mode=0700 %{buildroot}/%{_localstatedir}/log/%{srcname}
|
||||||
|
|
||||||
|
# Remove agent and webapp.
|
||||||
|
rm -f %{buildroot}/%{_bindir}/%{srcname}_agent
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/__pycache__/%{srcname}_agent*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/cmd/__pycache__/agent.*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/cmd/agent.*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/%{srcname}_agent.*
|
||||||
|
|
||||||
|
rm -f %{buildroot}/%{_bindir}/%{srcname}_webapp
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/__pycache__/tenant_webapp.*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/cmd/__pycache__/webapp.*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/cmd/webapp.*
|
||||||
|
rm -f %{buildroot}%{python3_sitelib}/%{srcname}/tenant_webapp.*
|
||||||
|
rm -rf %{buildroot}%{python3_sitelib}/%{srcname}/static/
|
||||||
|
|
||||||
|
# Remove misc progs.
|
||||||
|
rm -f %{buildroot}/%{_bindir}/%{srcname}_ima_emulator
|
||||||
|
rm -f %{buildroot}/%{_bindir}/%{srcname}_userdata_encrypt
|
||||||
|
|
||||||
|
# Disable zeromq revocation notifier, as there is no zeromq in RHEL.
|
||||||
|
# 6.4.1.
|
||||||
|
# Agent-Local revocation.
|
||||||
|
sed -e 's/^revocation_notifiers[[:space:]]*=.*/revocation_notifiers = agent/g' \
|
||||||
|
-i %{srcname}.conf
|
||||||
|
|
||||||
|
# Setting up the agent to use keylime:tss user/group after dropping privileges.
|
||||||
|
sed -e 's/^run_as[[:space:]]*=.*/run_as = keylime:tss/g' -i %{srcname}.conf
|
||||||
|
|
||||||
|
# Using sha256 for tpm_hash_alg.
|
||||||
|
sed -e 's/^tpm_hash_alg[[:space:]]*=.*/tpm_hash_alg = sha256/g' -i %{srcname}.conf
|
||||||
|
|
||||||
|
install -Dpm 600 %{srcname}.conf \
|
||||||
|
%{buildroot}%{_sysconfdir}/%{srcname}.conf
|
||||||
|
|
||||||
|
install -Dpm 644 ./services/%{srcname}_verifier.service \
|
||||||
|
%{buildroot}%{_unitdir}/%{srcname}_verifier.service
|
||||||
|
|
||||||
|
install -Dpm 644 ./services/%{srcname}_registrar.service \
|
||||||
|
%{buildroot}%{_unitdir}/%{srcname}_registrar.service
|
||||||
|
|
||||||
|
cp -r ./tpm_cert_store %{buildroot}%{_sharedstatedir}/keylime/
|
||||||
|
|
||||||
|
install -p -d %{buildroot}/%{_tmpfilesdir}
|
||||||
|
cat > %{buildroot}/%{_tmpfilesdir}/%{srcname}.conf << EOF
|
||||||
|
d %{_rundir}/%{srcname} 0700 %{srcname} %{srcname} -
|
||||||
|
EOF
|
||||||
|
|
||||||
|
install -p -D -m 0644 %{SOURCE1} %{buildroot}%{_sysusersdir}/%{srcname}.conf
|
||||||
|
|
||||||
|
%pre base
|
||||||
|
%sysusers_create_compat %{SOURCE1}
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
%posttrans base
|
||||||
|
[ -f %{_sysconfdir}/%{srcname}.conf ] && \
|
||||||
|
chmod 600 %{_sysconfdir}/%{srcname}.conf && \
|
||||||
|
chown %{srcname} %{_sysconfdir}/%{srcname}.conf
|
||||||
|
[ -d %{_sharedstatedir}/%{srcname} ] && \
|
||||||
|
chown -R %{srcname} %{_sharedstatedir}/%{srcname}/
|
||||||
|
[ -d %{_localstatedir}/log/%{srcname} ] && \
|
||||||
|
chown -R %{srcname} %{_localstatedir}/log/%{srcname}/
|
||||||
|
exit 0
|
||||||
|
|
||||||
|
%post verifier
|
||||||
|
%systemd_post %{srcname}_verifier.service
|
||||||
|
|
||||||
|
%post registrar
|
||||||
|
%systemd_post %{srcname}_registrar.service
|
||||||
|
|
||||||
|
%preun verifier
|
||||||
|
%systemd_preun %{srcname}_verifier.service
|
||||||
|
|
||||||
|
%preun registrar
|
||||||
|
%systemd_preun %{srcname}_registrar.service
|
||||||
|
|
||||||
|
%postun verifier
|
||||||
|
%systemd_postun_with_restart %{srcname}_verifier.service
|
||||||
|
|
||||||
|
%postun registrar
|
||||||
|
%systemd_postun_with_restart %{srcname}_registrar.service
|
||||||
|
|
||||||
|
%files verifier
|
||||||
|
%license LICENSE
|
||||||
|
%{_bindir}/%{srcname}_verifier
|
||||||
|
%{_bindir}/%{srcname}_ca
|
||||||
|
%{_bindir}/%{srcname}_migrations_apply
|
||||||
|
%{_unitdir}/keylime_verifier.service
|
||||||
|
|
||||||
|
%files registrar
|
||||||
|
%license LICENSE
|
||||||
|
%{_bindir}/%{srcname}_registrar
|
||||||
|
%{_unitdir}/keylime_registrar.service
|
||||||
|
|
||||||
|
%files tenant
|
||||||
|
%license LICENSE
|
||||||
|
%{_bindir}/%{srcname}_tenant
|
||||||
|
|
||||||
|
%files -n python3-%{srcname}
|
||||||
|
%license LICENSE
|
||||||
|
%{python3_sitelib}/%{srcname}-*.egg-info/
|
||||||
|
%{python3_sitelib}/%{srcname}
|
||||||
|
|
||||||
|
%files base
|
||||||
|
%license LICENSE keylime/static/icons/ICON-LICENSE
|
||||||
|
%doc README.md
|
||||||
|
%config(noreplace) %attr(600,%{srcname},%{srcname}) %{_sysconfdir}/%{srcname}.conf
|
||||||
|
%attr(700,%{srcname},%{srcname}) %dir %{_rundir}/%{srcname}
|
||||||
|
%attr(700,%{srcname},%{srcname}) %dir %{_localstatedir}/log/%{srcname}
|
||||||
|
%attr(700,%{srcname},%{srcname}) %{_sharedstatedir}/%{srcname}
|
||||||
|
%{_tmpfilesdir}/%{srcname}.conf
|
||||||
|
%{_sysusersdir}/%{srcname}.conf
|
||||||
|
|
||||||
|
%files
|
||||||
|
%license LICENSE
|
||||||
|
|
||||||
|
%changelog
|
||||||
|
%autochangelog
|
2
keylime.sysusers
Normal file
2
keylime.sysusers
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
u keylime - "Keylime unprivileged user" /var/lib/keylime /usr/sbin/nologin
|
||||||
|
m keylime tss
|
Loading…
Reference in New Issue
Block a user