Automatically update agent API version
Resolves: RHEL-1518 Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
This commit is contained in:
parent
2c457d5430
commit
86a18d1bb4
244
0011-Automatically-update-agent-API-version.patch
Normal file
244
0011-Automatically-update-agent-API-version.patch
Normal file
@ -0,0 +1,244 @@
|
||||
From b0cf69c9db20eb319ea2e90c22f500e09b704224 Mon Sep 17 00:00:00 2001
|
||||
From: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
||||
Date: Wed, 23 Aug 2023 16:24:15 +0200
|
||||
Subject: [PATCH] Implement automatic agent API version bump
|
||||
|
||||
Automatically update the agent supported API version in the database if
|
||||
the agent is updated and its API version is bumped.
|
||||
|
||||
Previously, if an agent was added to a verifier while it used an old API
|
||||
version, and then it is updated with an API version bump, the
|
||||
attestation would fail as the verifier would try to reach the agent
|
||||
using the old API version.
|
||||
|
||||
Fixes #1457
|
||||
|
||||
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
|
||||
---
|
||||
keylime/cloud_verifier_tornado.py | 185 +++++++++++++++++++++++++++---
|
||||
1 file changed, 167 insertions(+), 18 deletions(-)
|
||||
|
||||
diff --git a/keylime/cloud_verifier_tornado.py b/keylime/cloud_verifier_tornado.py
|
||||
index 261022ac6..31e6f7159 100644
|
||||
--- a/keylime/cloud_verifier_tornado.py
|
||||
+++ b/keylime/cloud_verifier_tornado.py
|
||||
@@ -32,6 +32,7 @@
|
||||
)
|
||||
from keylime.agentstates import AgentAttestState, AgentAttestStates
|
||||
from keylime.common import retry, states, validators
|
||||
+from keylime.common.version import str_to_version
|
||||
from keylime.da import record
|
||||
from keylime.db.keylime_db import DBEngineManager, SessionManager
|
||||
from keylime.db.verifier_db import VerfierMain, VerifierAllowlist
|
||||
@@ -998,6 +999,80 @@ def data_received(self, chunk: Any) -> None:
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
+async def update_agent_api_version(agent: Dict[str, Any], timeout: float = 60.0) -> Union[Dict[str, Any], None]:
|
||||
+ agent_id = agent["agent_id"]
|
||||
+
|
||||
+ logger.info("Agent %s API version bump detected, trying to update stored API version", agent_id)
|
||||
+ kwargs = {}
|
||||
+ if agent["ssl_context"]:
|
||||
+ kwargs["context"] = agent["ssl_context"]
|
||||
+
|
||||
+ res = tornado_requests.request(
|
||||
+ "GET",
|
||||
+ f"http://{agent['ip']}:{agent['port']}/version",
|
||||
+ **kwargs,
|
||||
+ timeout=timeout,
|
||||
+ )
|
||||
+ response = await res
|
||||
+
|
||||
+ if response.status_code != 200:
|
||||
+ logger.warning(
|
||||
+ "Could not get agent %s supported API version, Error: %s",
|
||||
+ agent["agent_id"],
|
||||
+ response.status_code,
|
||||
+ )
|
||||
+ return None
|
||||
+
|
||||
+ try:
|
||||
+ json_response = json.loads(response.body)
|
||||
+ new_version = json_response["results"]["supported_version"]
|
||||
+ old_version = agent["supported_version"]
|
||||
+
|
||||
+ # Only update the API version to use if it is supported by the verifier
|
||||
+ if new_version in keylime_api_version.all_versions():
|
||||
+ new_version_tuple = str_to_version(new_version)
|
||||
+ old_version_tuple = str_to_version(old_version)
|
||||
+
|
||||
+ assert new_version_tuple, f"Agent {agent_id} version {new_version} is invalid"
|
||||
+ assert old_version_tuple, f"Agent {agent_id} version {old_version} is invalid"
|
||||
+
|
||||
+ # Check that the new version is greater than current version
|
||||
+ if new_version_tuple <= old_version_tuple:
|
||||
+ logger.warning(
|
||||
+ "Agent %s API version %s is lower or equal to previous version %s",
|
||||
+ agent_id,
|
||||
+ new_version,
|
||||
+ old_version,
|
||||
+ )
|
||||
+ return None
|
||||
+
|
||||
+ logger.info("Agent %s new API version %s is supported", agent_id, new_version)
|
||||
+ session = get_session()
|
||||
+ agent["supported_version"] = new_version
|
||||
+
|
||||
+ # Remove keys that should not go to the DB
|
||||
+ agent_db = dict(agent)
|
||||
+ for key in exclude_db:
|
||||
+ if key in agent_db:
|
||||
+ del agent_db[key]
|
||||
+
|
||||
+ session.query(VerfierMain).filter_by(agent_id=agent_id).update(agent_db) # pyright: ignore
|
||||
+ session.commit()
|
||||
+ else:
|
||||
+ logger.warning("Agent %s new API version %s is not supported", agent_id, new_version)
|
||||
+ return None
|
||||
+
|
||||
+ except SQLAlchemyError as e:
|
||||
+ logger.error("SQLAlchemy Error updating API version for agent %s: %s", agent_id, e)
|
||||
+ return None
|
||||
+ except Exception as e:
|
||||
+ logger.exception(e)
|
||||
+ return None
|
||||
+
|
||||
+ logger.info("Agent %s API version updated to %s", agent["agent_id"], agent["supported_version"])
|
||||
+ return agent
|
||||
+
|
||||
+
|
||||
async def invoke_get_quote(
|
||||
agent: Dict[str, Any], runtime_policy: str, need_pubkey: bool, timeout: float = 60.0
|
||||
) -> None:
|
||||
@@ -1028,15 +1103,43 @@ async def invoke_get_quote(
|
||||
# this is a connection error, retry get quote
|
||||
if response.status_code in [408, 500, 599]:
|
||||
asyncio.ensure_future(process_agent(agent, states.GET_QUOTE_RETRY))
|
||||
- else:
|
||||
- # catastrophic error, do not continue
|
||||
- logger.critical(
|
||||
- "Unexpected Get Quote response error for cloud agent %s, Error: %s",
|
||||
- agent["agent_id"],
|
||||
- response.status_code,
|
||||
- )
|
||||
- failure.add_event("no_quote", "Unexpected Get Quote reponse from agent", False)
|
||||
- asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ if response.status_code == 400:
|
||||
+ try:
|
||||
+ json_response = json.loads(response.body)
|
||||
+ if "API version not supported" in json_response["status"]:
|
||||
+ update = update_agent_api_version(agent)
|
||||
+ updated = await update
|
||||
+
|
||||
+ if updated:
|
||||
+ asyncio.ensure_future(process_agent(updated, states.GET_QUOTE_RETRY))
|
||||
+ else:
|
||||
+ logger.warning("Could not update stored agent %s API version", agent["agent_id"])
|
||||
+ failure.add_event(
|
||||
+ "version_not_supported",
|
||||
+ {"context": "Agent API version not supported", "data": json_response},
|
||||
+ False,
|
||||
+ )
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ except Exception as e:
|
||||
+ logger.exception(e)
|
||||
+ failure.add_event(
|
||||
+ "exception", {"context": "Agent caused the verifier to throw an exception", "data": str(e)}, False
|
||||
+ )
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ # catastrophic error, do not continue
|
||||
+ logger.critical(
|
||||
+ "Unexpected Get Quote response error for cloud agent %s, Error: %s",
|
||||
+ agent["agent_id"],
|
||||
+ response.status_code,
|
||||
+ )
|
||||
+ failure.add_event("no_quote", "Unexpected Get Quote reponse from agent", False)
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
else:
|
||||
try:
|
||||
json_response = json.loads(response.body)
|
||||
@@ -1100,15 +1203,43 @@ async def invoke_provide_v(agent: Dict[str, Any], timeout: float = 60.0) -> None
|
||||
if response.status_code != 200:
|
||||
if response.status_code in [408, 500, 599]:
|
||||
asyncio.ensure_future(process_agent(agent, states.PROVIDE_V_RETRY))
|
||||
- else:
|
||||
- # catastrophic error, do not continue
|
||||
- logger.critical(
|
||||
- "Unexpected Provide V response error for cloud agent %s, Error: %s",
|
||||
- agent["agent_id"],
|
||||
- response.status_code,
|
||||
- )
|
||||
- failure.add_event("no_v", {"message": "Unexpected provide V response", "data": response.status_code}, False)
|
||||
- asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ if response.status_code == 400:
|
||||
+ try:
|
||||
+ json_response = json.loads(response.body)
|
||||
+ if "API version not supported" in json_response["status"]:
|
||||
+ update = update_agent_api_version(agent)
|
||||
+ updated = await update
|
||||
+
|
||||
+ if updated:
|
||||
+ asyncio.ensure_future(process_agent(updated, states.PROVIDE_V_RETRY))
|
||||
+ else:
|
||||
+ logger.warning("Could not update stored agent %s API version", agent["agent_id"])
|
||||
+ failure.add_event(
|
||||
+ "version_not_supported",
|
||||
+ {"context": "Agent API version not supported", "data": json_response},
|
||||
+ False,
|
||||
+ )
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ except Exception as e:
|
||||
+ logger.exception(e)
|
||||
+ failure.add_event(
|
||||
+ "exception", {"context": "Agent caused the verifier to throw an exception", "data": str(e)}, False
|
||||
+ )
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
+ return
|
||||
+
|
||||
+ # catastrophic error, do not continue
|
||||
+ logger.critical(
|
||||
+ "Unexpected Provide V response error for cloud agent %s, Error: %s",
|
||||
+ agent["agent_id"],
|
||||
+ response.status_code,
|
||||
+ )
|
||||
+ failure.add_event("no_v", {"message": "Unexpected provide V response", "data": response.status_code}, False)
|
||||
+ asyncio.ensure_future(process_agent(agent, states.FAILED, failure))
|
||||
else:
|
||||
asyncio.ensure_future(process_agent(agent, states.GET_QUOTE))
|
||||
|
||||
@@ -1134,6 +1265,24 @@ async def invoke_notify_error(agent: Dict[str, Any], tosend: Dict[str, Any], tim
|
||||
agent["agent_id"],
|
||||
)
|
||||
elif response.status_code != 200:
|
||||
+ if response.status_code == 400:
|
||||
+ try:
|
||||
+ json_response = json.loads(response.body)
|
||||
+ if "API version not supported" in json_response["status"]:
|
||||
+ update = update_agent_api_version(agent)
|
||||
+ updated = await update
|
||||
+
|
||||
+ if updated:
|
||||
+ asyncio.ensure_future(invoke_notify_error(updated, tosend))
|
||||
+ else:
|
||||
+ logger.warning("Could not update stored agent %s API version", agent["agent_id"])
|
||||
+
|
||||
+ return
|
||||
+
|
||||
+ except Exception as e:
|
||||
+ logger.exception(e)
|
||||
+ return
|
||||
+
|
||||
logger.warning(
|
||||
"Unexpected Notify Revocation response error for cloud agent %s, Error: %s",
|
||||
agent["agent_id"],
|
@ -9,7 +9,7 @@
|
||||
|
||||
Name: keylime
|
||||
Version: 7.3.0
|
||||
Release: 6%{?dist}
|
||||
Release: 7%{?dist}
|
||||
Summary: Open source TPM software for Bootstrapping and Maintaining Trust
|
||||
|
||||
URL: https://github.com/keylime/keylime
|
||||
@ -27,6 +27,7 @@ Patch: 0007-Handle-session-close-using-a-session-manager.patch
|
||||
Patch: 0008-verifier-should-read-parameters-from-verifier.conf-o.patch
|
||||
Patch: 0009-CVE-2023-38201.patch
|
||||
Patch: 0010-CVE-2023-38200.patch
|
||||
Patch: 0011-Automatically-update-agent-API-version.patch
|
||||
|
||||
License: ASL 2.0 and MIT
|
||||
|
||||
@ -359,6 +360,10 @@ fi
|
||||
%license LICENSE
|
||||
|
||||
%changelog
|
||||
* Mon Aug 28 2023 Anderson Toshiyuki Sasaki <ansasaki@redhat.com> - 7.3.0-7
|
||||
- Automatically update agent API version
|
||||
Resolves: RHEL-1518
|
||||
|
||||
* Mon Aug 28 2023 Sergio Correia <scorreia@redhat.com> - 7.3.0-6
|
||||
- Fix registrar is subject to a DoS against SSL (CVE-2023-38200)
|
||||
Resolves: rhbz#2222694
|
||||
|
Loading…
Reference in New Issue
Block a user