mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-27 03:21:57 -06:00
More changes
This commit is contained in:
@@ -141,6 +141,18 @@ def register(
|
||||
# Another device already claims this hostname; keep the existing
|
||||
# canonical hostname assigned during enrollment to avoid breaking
|
||||
# the unique constraint and continue updating the remaining fields.
|
||||
existing_guid_for_hostname: Optional[str] = None
|
||||
if "hostname" in updates:
|
||||
try:
|
||||
cur.execute(
|
||||
"SELECT guid FROM devices WHERE hostname = ?",
|
||||
(updates["hostname"],),
|
||||
)
|
||||
row = cur.fetchone()
|
||||
if row and row[0]:
|
||||
existing_guid_for_hostname = normalize_guid(row[0])
|
||||
except Exception:
|
||||
existing_guid_for_hostname = None
|
||||
if "hostname" in updates:
|
||||
updates.pop("hostname", None)
|
||||
try:
|
||||
@@ -148,12 +160,23 @@ def register(
|
||||
except sqlite3.IntegrityError:
|
||||
raise
|
||||
else:
|
||||
log(
|
||||
"server",
|
||||
"heartbeat hostname collision ignored for guid="
|
||||
f"{ctx.guid}",
|
||||
context_label,
|
||||
)
|
||||
try:
|
||||
current_guid = normalize_guid(ctx.guid)
|
||||
except Exception:
|
||||
current_guid = ctx.guid
|
||||
if (
|
||||
existing_guid_for_hostname
|
||||
and current_guid
|
||||
and existing_guid_for_hostname == current_guid
|
||||
):
|
||||
pass # Same device contexts; no log needed.
|
||||
else:
|
||||
log(
|
||||
"server",
|
||||
"heartbeat hostname collision ignored for guid="
|
||||
f"{ctx.guid}",
|
||||
context_label,
|
||||
)
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
@@ -19,13 +19,18 @@ from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import ec
|
||||
from cryptography.x509.oid import NameOID
|
||||
|
||||
from Modules.runtime import ensure_runtime_dir, runtime_path
|
||||
from Modules.runtime import ensure_server_certificates_dir, server_certificates_path, runtime_path
|
||||
|
||||
_CERT_DIR = runtime_path("certs")
|
||||
_CERT_DIR = server_certificates_path()
|
||||
_CERT_FILE = _CERT_DIR / "borealis-server-cert.pem"
|
||||
_KEY_FILE = _CERT_DIR / "borealis-server-key.pem"
|
||||
_BUNDLE_FILE = _CERT_DIR / "borealis-server-bundle.pem"
|
||||
|
||||
_LEGACY_CERT_DIR = runtime_path("certs")
|
||||
_LEGACY_CERT_FILE = _LEGACY_CERT_DIR / "borealis-server-cert.pem"
|
||||
_LEGACY_KEY_FILE = _LEGACY_CERT_DIR / "borealis-server-key.pem"
|
||||
_LEGACY_BUNDLE_FILE = _LEGACY_CERT_DIR / "borealis-server-bundle.pem"
|
||||
|
||||
# 100-year lifetime (effectively "never" for self-signed deployments).
|
||||
_CERT_VALIDITY = timedelta(days=365 * 100)
|
||||
|
||||
@@ -37,7 +42,8 @@ def ensure_certificate(common_name: str = "Borealis Server") -> Tuple[Path, Path
|
||||
Returns (cert_path, key_path, bundle_path).
|
||||
"""
|
||||
|
||||
ensure_runtime_dir("certs")
|
||||
ensure_server_certificates_dir()
|
||||
_migrate_legacy_material_if_present()
|
||||
|
||||
regenerate = not (_CERT_FILE.exists() and _KEY_FILE.exists())
|
||||
if not regenerate:
|
||||
@@ -62,6 +68,38 @@ def ensure_certificate(common_name: str = "Borealis Server") -> Tuple[Path, Path
|
||||
return _CERT_FILE, _KEY_FILE, _BUNDLE_FILE
|
||||
|
||||
|
||||
def _migrate_legacy_material_if_present() -> None:
|
||||
if _CERT_FILE.exists() and _KEY_FILE.exists():
|
||||
return
|
||||
|
||||
legacy_cert = _LEGACY_CERT_FILE
|
||||
legacy_key = _LEGACY_KEY_FILE
|
||||
legacy_bundle = _LEGACY_BUNDLE_FILE
|
||||
|
||||
if not legacy_cert.exists() or not legacy_key.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
ensure_server_certificates_dir()
|
||||
if not _CERT_FILE.exists():
|
||||
try:
|
||||
legacy_cert.replace(_CERT_FILE)
|
||||
except Exception:
|
||||
_CERT_FILE.write_bytes(legacy_cert.read_bytes())
|
||||
if not _KEY_FILE.exists():
|
||||
try:
|
||||
legacy_key.replace(_KEY_FILE)
|
||||
except Exception:
|
||||
_KEY_FILE.write_bytes(legacy_key.read_bytes())
|
||||
if legacy_bundle.exists() and not _BUNDLE_FILE.exists():
|
||||
try:
|
||||
legacy_bundle.replace(_BUNDLE_FILE)
|
||||
except Exception:
|
||||
_BUNDLE_FILE.write_bytes(legacy_bundle.read_bytes())
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
def _generate_certificate(common_name: str) -> None:
|
||||
private_key = ec.generate_private_key(ec.SECP384R1())
|
||||
public_key = private_key.public_key()
|
||||
|
||||
@@ -10,15 +10,22 @@ 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 Modules.runtime import (
|
||||
ensure_server_certificates_dir,
|
||||
server_certificates_path,
|
||||
runtime_path,
|
||||
)
|
||||
|
||||
from .keys import base64_from_spki_der
|
||||
|
||||
_KEY_DIR = runtime_path("script_signing_keys")
|
||||
_KEY_DIR = server_certificates_path("Code-Signing")
|
||||
_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"
|
||||
_OLD_RUNTIME_KEY_DIR = runtime_path("script_signing_keys")
|
||||
_OLD_RUNTIME_KEY_FILE = _OLD_RUNTIME_KEY_DIR / "borealis-script-ed25519.key"
|
||||
_OLD_RUNTIME_PUB_FILE = _OLD_RUNTIME_KEY_DIR / "borealis-script-ed25519.pub"
|
||||
|
||||
|
||||
class ScriptSigner:
|
||||
@@ -45,7 +52,7 @@ def load_signer() -> ScriptSigner:
|
||||
|
||||
|
||||
def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
||||
ensure_runtime_dir("script_signing_keys")
|
||||
ensure_server_certificates_dir("Code-Signing")
|
||||
_migrate_legacy_material_if_present()
|
||||
|
||||
if _SIGNING_KEY_FILE.exists():
|
||||
@@ -80,11 +87,30 @@ def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
||||
|
||||
|
||||
def _migrate_legacy_material_if_present() -> None:
|
||||
if _SIGNING_KEY_FILE.exists():
|
||||
return
|
||||
|
||||
# First migrate from legacy runtime path embedded in Server runtime.
|
||||
try:
|
||||
if _OLD_RUNTIME_KEY_FILE.exists() and not _SIGNING_KEY_FILE.exists():
|
||||
ensure_server_certificates_dir("Code-Signing")
|
||||
try:
|
||||
_OLD_RUNTIME_KEY_FILE.replace(_SIGNING_KEY_FILE)
|
||||
except Exception:
|
||||
_SIGNING_KEY_FILE.write_bytes(_OLD_RUNTIME_KEY_FILE.read_bytes())
|
||||
if _OLD_RUNTIME_PUB_FILE.exists() and not _SIGNING_PUB_FILE.exists():
|
||||
try:
|
||||
_OLD_RUNTIME_PUB_FILE.replace(_SIGNING_PUB_FILE)
|
||||
except Exception:
|
||||
_SIGNING_PUB_FILE.write_bytes(_OLD_RUNTIME_PUB_FILE.read_bytes())
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if not _LEGACY_KEY_FILE.exists() or _SIGNING_KEY_FILE.exists():
|
||||
return
|
||||
|
||||
try:
|
||||
ensure_runtime_dir("script_signing_keys")
|
||||
ensure_server_certificates_dir("Code-Signing")
|
||||
try:
|
||||
_LEGACY_KEY_FILE.replace(_SIGNING_KEY_FILE)
|
||||
except Exception:
|
||||
@@ -97,4 +123,3 @@ def _migrate_legacy_material_if_present() -> None:
|
||||
_SIGNING_PUB_FILE.write_bytes(_LEGACY_PUB_FILE.read_bytes())
|
||||
except Exception:
|
||||
return
|
||||
|
||||
|
||||
@@ -82,13 +82,47 @@ def ensure_runtime_dir(*parts: str) -> Path:
|
||||
def certificates_root() -> Path:
|
||||
"""Base directory for persisted certificate material."""
|
||||
|
||||
env = _env_path("BOREALIS_CERT_ROOT")
|
||||
env = _env_path("BOREALIS_CERTIFICATES_ROOT") or _env_path("BOREALIS_CERT_ROOT")
|
||||
if env:
|
||||
env.mkdir(parents=True, exist_ok=True)
|
||||
return env
|
||||
|
||||
root = project_root() / "Certificates"
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
# Ensure expected subdirectories exist for agent and server material.
|
||||
try:
|
||||
(root / "Server").mkdir(parents=True, exist_ok=True)
|
||||
(root / "Agent").mkdir(parents=True, exist_ok=True)
|
||||
except Exception:
|
||||
pass
|
||||
return root
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def server_certificates_root() -> Path:
|
||||
"""Base directory for server certificate material."""
|
||||
|
||||
env = _env_path("BOREALIS_SERVER_CERT_ROOT")
|
||||
if env:
|
||||
env.mkdir(parents=True, exist_ok=True)
|
||||
return env
|
||||
|
||||
root = certificates_root() / "Server"
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
return root
|
||||
|
||||
|
||||
@lru_cache(maxsize=None)
|
||||
def agent_certificates_root() -> Path:
|
||||
"""Base directory for agent certificate material."""
|
||||
|
||||
env = _env_path("BOREALIS_AGENT_CERT_ROOT")
|
||||
if env:
|
||||
env.mkdir(parents=True, exist_ok=True)
|
||||
return env
|
||||
|
||||
root = certificates_root() / "Agent"
|
||||
root.mkdir(parents=True, exist_ok=True)
|
||||
return root
|
||||
|
||||
|
||||
@@ -104,3 +138,31 @@ def ensure_certificates_dir(*parts: str) -> Path:
|
||||
path = certificates_path(*parts)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def server_certificates_path(*parts: str) -> Path:
|
||||
"""Return a path under the server certificates root."""
|
||||
|
||||
return server_certificates_root().joinpath(*parts)
|
||||
|
||||
|
||||
def ensure_server_certificates_dir(*parts: str) -> Path:
|
||||
"""Create (if required) and return a server certificates subdirectory."""
|
||||
|
||||
path = server_certificates_path(*parts)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
def agent_certificates_path(*parts: str) -> Path:
|
||||
"""Return a path under the agent certificates root."""
|
||||
|
||||
return agent_certificates_root().joinpath(*parts)
|
||||
|
||||
|
||||
def ensure_agent_certificates_dir(*parts: str) -> Path:
|
||||
"""Create (if required) and return an agent certificates subdirectory."""
|
||||
|
||||
path = agent_certificates_path(*parts)
|
||||
path.mkdir(parents=True, exist_ok=True)
|
||||
return path
|
||||
|
||||
Reference in New Issue
Block a user