mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-27 08:21:57 -06:00
Fix agent keystore initialization order
This commit is contained in:
@@ -7,15 +7,17 @@ from __future__ import annotations
|
||||
import hashlib
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
import jwt
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||
|
||||
_KEY_DIR = Path(__file__).resolve().parent.parent / "keys"
|
||||
from Modules.runtime import ensure_runtime_dir, runtime_path
|
||||
|
||||
_KEY_DIR = runtime_path("auth_keys")
|
||||
_KEY_FILE = _KEY_DIR / "borealis-jwt-ed25519.key"
|
||||
_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-jwt-ed25519.key"
|
||||
|
||||
|
||||
class JWTService:
|
||||
@@ -96,11 +98,17 @@ def load_service() -> JWTService:
|
||||
|
||||
|
||||
def _load_or_create_private_key() -> ed25519.Ed25519PrivateKey:
|
||||
_KEY_DIR.mkdir(parents=True, exist_ok=True)
|
||||
ensure_runtime_dir("auth_keys")
|
||||
_migrate_legacy_key_if_present()
|
||||
|
||||
if _KEY_FILE.exists():
|
||||
with _KEY_FILE.open("rb") as fh:
|
||||
return serialization.load_pem_private_key(fh.read(), password=None)
|
||||
|
||||
if _LEGACY_KEY_FILE.exists():
|
||||
with _LEGACY_KEY_FILE.open("rb") as fh:
|
||||
return serialization.load_pem_private_key(fh.read(), password=None)
|
||||
|
||||
private_key = ed25519.Ed25519PrivateKey.generate()
|
||||
pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
@@ -116,3 +124,17 @@ def _load_or_create_private_key() -> ed25519.Ed25519PrivateKey:
|
||||
pass
|
||||
return private_key
|
||||
|
||||
|
||||
def _migrate_legacy_key_if_present() -> None:
|
||||
if not _LEGACY_KEY_FILE.exists() or _KEY_FILE.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
ensure_runtime_dir("auth_keys")
|
||||
try:
|
||||
_LEGACY_KEY_FILE.replace(_KEY_FILE)
|
||||
except Exception:
|
||||
_KEY_FILE.write_bytes(_LEGACY_KEY_FILE.read_bytes())
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
@@ -19,7 +19,9 @@ from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.x509.oid import NameOID
|
||||
|
||||
_CERT_DIR = Path(__file__).resolve().parent.parent / "certs"
|
||||
from Modules.runtime import ensure_runtime_dir, runtime_path
|
||||
|
||||
_CERT_DIR = runtime_path("certs")
|
||||
_CERT_FILE = _CERT_DIR / "borealis-server-cert.pem"
|
||||
_KEY_FILE = _CERT_DIR / "borealis-server-key.pem"
|
||||
_BUNDLE_FILE = _CERT_DIR / "borealis-server-bundle.pem"
|
||||
@@ -35,7 +37,7 @@ def ensure_certificate(common_name: str = "Borealis Server") -> Tuple[Path, Path
|
||||
Returns (cert_path, key_path, bundle_path).
|
||||
"""
|
||||
|
||||
_CERT_DIR.mkdir(parents=True, exist_ok=True)
|
||||
ensure_runtime_dir("certs")
|
||||
|
||||
regenerate = not (_CERT_FILE.exists() and _KEY_FILE.exists())
|
||||
if not regenerate:
|
||||
|
||||
@@ -10,11 +10,15 @@ from typing import Tuple
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
||||
|
||||
from Modules.runtime import ensure_runtime_dir, runtime_path
|
||||
|
||||
from .keys import base64_from_spki_der
|
||||
|
||||
_KEY_DIR = Path(__file__).resolve().parent.parent / "keys"
|
||||
_KEY_DIR = runtime_path("script_signing_keys")
|
||||
_SIGNING_KEY_FILE = _KEY_DIR / "borealis-script-ed25519.key"
|
||||
_SIGNING_PUB_FILE = _KEY_DIR / "borealis-script-ed25519.pub"
|
||||
_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-script-ed25519.key"
|
||||
_LEGACY_PUB_FILE = runtime_path("keys") / "borealis-script-ed25519.pub"
|
||||
|
||||
|
||||
class ScriptSigner:
|
||||
@@ -41,11 +45,17 @@ def load_signer() -> ScriptSigner:
|
||||
|
||||
|
||||
def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
||||
_KEY_DIR.mkdir(parents=True, exist_ok=True)
|
||||
ensure_runtime_dir("script_signing_keys")
|
||||
_migrate_legacy_material_if_present()
|
||||
|
||||
if _SIGNING_KEY_FILE.exists():
|
||||
with _SIGNING_KEY_FILE.open("rb") as fh:
|
||||
return serialization.load_pem_private_key(fh.read(), password=None)
|
||||
|
||||
if _LEGACY_KEY_FILE.exists():
|
||||
with _LEGACY_KEY_FILE.open("rb") as fh:
|
||||
return serialization.load_pem_private_key(fh.read(), password=None)
|
||||
|
||||
private_key = ed25519.Ed25519PrivateKey.generate()
|
||||
pem = private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
@@ -68,3 +78,23 @@ def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
||||
|
||||
return private_key
|
||||
|
||||
|
||||
def _migrate_legacy_material_if_present() -> None:
|
||||
if not _LEGACY_KEY_FILE.exists() or _SIGNING_KEY_FILE.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
ensure_runtime_dir("script_signing_keys")
|
||||
try:
|
||||
_LEGACY_KEY_FILE.replace(_SIGNING_KEY_FILE)
|
||||
except Exception:
|
||||
_SIGNING_KEY_FILE.write_bytes(_LEGACY_KEY_FILE.read_bytes())
|
||||
|
||||
if _LEGACY_PUB_FILE.exists() and not _SIGNING_PUB_FILE.exists():
|
||||
try:
|
||||
_LEGACY_PUB_FILE.replace(_SIGNING_PUB_FILE)
|
||||
except Exception:
|
||||
_SIGNING_PUB_FILE.write_bytes(_LEGACY_PUB_FILE.read_bytes())
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
78
Data/Server/Modules/runtime.py
Normal file
78
Data/Server/Modules/runtime.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Utility helpers for locating runtime storage paths.
|
||||
|
||||
The Borealis repository keeps the authoritative source code under ``Data/``
|
||||
so that the bootstrap scripts can copy those assets into sibling ``Server/``
|
||||
and ``Agent/`` directories for execution. Runtime artefacts such as TLS
|
||||
certificates or signing keys must therefore live outside ``Data`` to avoid
|
||||
polluting the template tree. This module centralises the path selection so
|
||||
other modules can rely on a consistent location regardless of whether they
|
||||
are executed from the copied runtime directory or directly from ``Data``
|
||||
during development.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from functools import lru_cache
|
||||
from pathlib import Path
|
||||
from typing import Optional
|
||||
|
||||
|
||||
def _env_path(name: str) -> Optional[Path]:
|
||||
"""Return a resolved ``Path`` for the given environment variable."""
|
||||
|
||||
value = os.environ.get(name)
|
||||
if not value:
|
||||
return None
|
||||
try:
|
||||
return Path(value).expanduser().resolve()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def project_root() -> Path:
|
||||
"""Best-effort detection of the repository root."""
|
||||
|
||||
env = _env_path("BOREALIS_PROJECT_ROOT")
|
||||
if env:
|
||||
return env
|
||||
|
||||
current = Path(__file__).resolve()
|
||||
for parent in current.parents:
|
||||
if (parent / "Borealis.ps1").exists() or (parent / ".git").is_dir():
|
||||
return parent
|
||||
|
||||
# Fallback to the ancestor that corresponds to ``<repo>/`` when the module
|
||||
# lives under ``Data/Server/Modules``.
|
||||
try:
|
||||
return current.parents[4]
|
||||
except IndexError:
|
||||
return current.parent
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def server_runtime_root() -> Path:
|
||||
"""Location where the running server stores mutable artefacts."""
|
||||
|
||||
env = _env_path("BOREALIS_SERVER_ROOT")
|
||||
if env:
|
||||
return env
|
||||
|
||||
root = project_root()
|
||||
runtime = root / "Server" / "Borealis"
|
||||
return runtime
|
||||
|
||||
|
||||
def runtime_path(*parts: str) -> Path:
|
||||
"""Return a path relative to the server runtime root."""
|
||||
|
||||
return server_runtime_root().joinpath(*parts)
|
||||
|
||||
|
||||
def ensure_runtime_dir(*parts: str) -> Path:
|
||||
"""Create (if required) and return a runtime directory."""
|
||||
|
||||
path = runtime_path(*parts)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
Reference in New Issue
Block a user