summaryrefslogtreecommitdiff
path: root/0011-Automatically-update-agent-API-version.patch
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2024-08-06 02:17:30 +0000
committerCoprDistGit <infra@openeuler.org>2024-08-06 02:17:30 +0000
commit35db127c4920388f07b1c109a88e6845d80ec827 (patch)
treea0f1670b1f0d4b49baf63986bc4968f33cfc6250 /0011-Automatically-update-agent-API-version.patch
parent192f645be293b6bad64875fb1cfb872b027d99be (diff)
automatic import of keylimeopeneuler24.03_LTS
Diffstat (limited to '0011-Automatically-update-agent-API-version.patch')
-rw-r--r--0011-Automatically-update-agent-API-version.patch244
1 files changed, 244 insertions, 0 deletions
diff --git a/0011-Automatically-update-agent-API-version.patch b/0011-Automatically-update-agent-API-version.patch
new file mode 100644
index 0000000..c87b309
--- /dev/null
+++ b/0011-Automatically-update-agent-API-version.patch
@@ -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"],