Merge pull request #125 from bunny-lab-io:codex/review-and-resolve-enrollment-implementation-issues

Fix runtime storage paths for TLS and signing assets
This commit is contained in:
2025-10-17 20:44:04 -06:00
committed by GitHub
8 changed files with 190 additions and 13 deletions

View File

@@ -784,13 +784,19 @@ function InstallOrUpdate-BorealisAgent {
# Copy Agent Files to Virtual Python Environment
$coreAgentFiles = @(
(Join-Path $agentSourceRoot 'agent.py'),
(Join-Path $agentSourceRoot 'Python_API_Endpoints'),
(Join-Path $agentSourceRoot 'Roles'),
(Join-Path $agentSourceRoot 'Scripts'),
(Join-Path $agentSourceRoot 'agent_deployment.py'),
(Join-Path $agentSourceRoot 'agent.py'),
(Join-Path $agentSourceRoot 'ansible-ee-version.txt'),
(Join-Path $agentSourceRoot 'Borealis.ico'),
(Join-Path $agentSourceRoot 'fcntl_stub.py'),
(Join-Path $agentSourceRoot 'launch_service.ps1'),
(Join-Path $agentSourceRoot 'role_manager.py'),
(Join-Path $agentSourceRoot 'Roles')
(Join-Path $agentSourceRoot 'security.py'),
(Join-Path $agentSourceRoot 'sitecustomize.py'),
(Join-Path $agentSourceRoot 'termios_stub.py')
)
Copy-Item $coreAgentFiles -Destination $agentDestinationFolder -Recurse -Force

View File

@@ -1,2 +1 @@
#////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/Data/Agent/ansible-ee-version.txt
1.0.0
1.0.0

View File

@@ -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

View File

@@ -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:

View File

@@ -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

View 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

View File

@@ -4,16 +4,20 @@ import react from '@vitejs/plugin-react';
import path from 'path';
import fs from 'fs';
const runtimeCertDir = process.env.BOREALIS_CERT_DIR;
const certCandidates = [
process.env.BOREALIS_TLS_CERT,
runtimeCertDir && path.resolve(runtimeCertDir, 'borealis-server-cert.pem'),
path.resolve(__dirname, '../certs/borealis-server-cert.pem'),
path.resolve(__dirname, '../../Data/Server/certs/borealis-server-cert.pem'),
path.resolve(__dirname, '../../../Server/Borealis/certs/borealis-server-cert.pem'),
] as const;
const keyCandidates = [
process.env.BOREALIS_TLS_KEY,
runtimeCertDir && path.resolve(runtimeCertDir, 'borealis-server-key.pem'),
path.resolve(__dirname, '../certs/borealis-server-key.pem'),
path.resolve(__dirname, '../../Data/Server/certs/borealis-server-key.pem'),
path.resolve(__dirname, '../../../Server/Borealis/certs/borealis-server-key.pem'),
] as const;
const pickFirst = (candidates: readonly (string | undefined)[]) => {

View File

@@ -20,6 +20,8 @@ Section Guide:
import os
import sys
from pathlib import Path
import ssl
# Ensure the modular server package is importable when the runtime is launched
# from a packaged directory (e.g., Server/Borealis). We look for the canonical
@@ -40,6 +42,36 @@ eventlet.monkey_patch(thread=False)
from eventlet import tpool
try:
from eventlet.wsgi import HttpProtocol # type: ignore
except Exception:
HttpProtocol = None # type: ignore[assignment]
else:
_original_handle_one_request = HttpProtocol.handle_one_request
def _quiet_tls_http_mismatch(self): # type: ignore[override]
try:
return _original_handle_one_request(self)
except ssl.SSLError as exc: # type: ignore[arg-type]
reason = getattr(exc, "reason", "")
reason_text = str(reason).lower() if reason else ""
message = " ".join(str(arg) for arg in exc.args if arg).lower()
if "http_request" in message or reason_text == "http request":
try:
self.close_connection = True # type: ignore[attr-defined]
except Exception:
pass
try:
conn = getattr(self, "socket", None) or getattr(self, "connection", None)
if conn:
conn.close()
except Exception:
pass
return None
raise
HttpProtocol.handle_one_request = _quiet_tls_http_mismatch # type: ignore[assignment]
import requests
import re
import base64
@@ -167,6 +199,10 @@ TLS_CERT_PATH, TLS_KEY_PATH, TLS_BUNDLE_PATH = certificates.certificate_paths()
os.environ.setdefault("BOREALIS_TLS_CERT", TLS_CERT_PATH)
os.environ.setdefault("BOREALIS_TLS_KEY", TLS_KEY_PATH)
os.environ.setdefault("BOREALIS_TLS_BUNDLE", TLS_BUNDLE_PATH)
try:
os.environ.setdefault("BOREALIS_CERT_DIR", str(Path(TLS_CERT_PATH).resolve().parent))
except Exception:
pass
JWT_SERVICE = jwt_service_module.load_service()
SCRIPT_SIGNER = signing.load_signer()