Skip to content

Commit ba04844

Browse files
feat(compute): provision a node encryption key for local hypervisor agents
The local-pool-agent's self-registration to status_api/orch_api was failing with a 500: unencrypted communication never sends the X-Genesis-Node-UUID header the SDK middleware requires, so it can't work regardless of endpoint. Provision a NodeEncryptionKey for the agent's node uuid when a MachinePool is created for the exordos_local_hyper kind, accepting a pre-generated key so the bootstrap flow (which writes the same key to the agent's disk) stays in sync instead of getting a mismatched one. Also expose the key via a get_agent_private_key action for the non-bootstrap registration path.
1 parent 1b33a8d commit ba04844

4 files changed

Lines changed: 43 additions & 1 deletion

File tree

exordos_core/bootstrap/defaults.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ def apply_startup_db(spec: dict[str, tp.Any]) -> None:
393393

394394
for hypervisor in stand.get("hypervisors", []):
395395
hypervisor["iface_mtu"] = 1500
396+
# The agent encryption key isn't part of the driver spec, it's the
397+
# symmetric key already written to the local agent's disk by the
398+
# CLI, so it must be popped off before the driver spec is built.
399+
agent_private_key = hypervisor.pop("private_key", None)
396400
# Drop unset optional fields (e.g. `node`, only used by the
397401
# "exordos_local_hyper" kind) so they don't trip up spec kinds
398402
# that don't define them.
@@ -411,7 +415,7 @@ def apply_startup_db(spec: dict[str, tp.Any]) -> None:
411415
else:
412416
# Pool does not exist, create it
413417
try:
414-
pool.insert()
418+
pool.insert(agent_private_key=agent_private_key)
415419
except ra_exceptions.ConflictRecords:
416420
LOG.info("Machine pool %s already exists", pool.uuid)
417421
else:

exordos_core/compute/dm/models.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,30 @@ class MachinePool(
110110
default=list,
111111
)
112112

113+
def insert(self, session=None, agent_private_key: str | None = None):
114+
super().insert(session=session)
115+
116+
# A local hypervisor's agent authenticates to the orch/status APIs
117+
# with a node encryption key, so provision one for its node uuid.
118+
# `agent_private_key` lets a caller that already generated and
119+
# deployed a key to the agent (e.g. the bootstrap flow) keep both
120+
# sides in sync instead of getting a fresh, mismatched one here.
121+
if isinstance(self.driver_spec, ExordosLocalHyperDriverSpec):
122+
if agent_private_key is None:
123+
_, agent_private_key = ua_crypto.generate_key_base64()
124+
private_key = ua_models.NodeEncryptionKey(
125+
uuid=self.driver_spec.node,
126+
private_key=agent_private_key,
127+
)
128+
private_key.insert(session=session)
129+
130+
def get_agent_private_key(self):
131+
enc_key = ua_models.NodeEncryptionKey.objects.get_one(
132+
filters={"uuid": dm_filters.EQ(self.driver_spec.node)}
133+
)
134+
135+
return enc_key.private_key
136+
113137

114138
class Volume(
115139
infra_models.Volume,

exordos_core/user_api/compute/api/controllers.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,3 +235,9 @@ def _validate_driver_spec_uniqueness(self, kwargs: dict) -> None:
235235
model="MachinePool",
236236
msg=f"node={node}",
237237
)
238+
239+
@actions.get
240+
def get_agent_private_key(self, resource: models.MachinePool):
241+
self._enforce("get_agent_private_key")
242+
243+
return resource.get_agent_private_key()

exordos_core/user_api/compute/api/routes.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,19 @@ class NodeRoute(routes.Route):
5454
get_private_key = routes.action(NodePrivateKeyActionRoute)
5555

5656

57+
class HypervisorAgentPrivateKeyActionRoute(routes.Action):
58+
"""Handler for /v1/compute/hypervisors/<uuid>/actions/get_agent_private_key/invoke endpoint"""
59+
60+
__controller__ = controllers.HypervisorsController
61+
62+
5763
class HypervisorRoute(routes.Route):
5864
"""Handler for /v1/compute/hypervisors/ endpoint"""
5965

6066
__controller__ = controllers.HypervisorsController
6167

68+
get_agent_private_key = routes.action(HypervisorAgentPrivateKeyActionRoute)
69+
6270

6371
class NodeSetPrivateKeyActionRoute(routes.Action):
6472
"""Handler for /v1/compute/sets/<uuid>/actions/get_private_keys/invoke endpoint"""

0 commit comments

Comments
 (0)