mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-10-26 15:21:57 -06:00
More changes
This commit is contained in:
10
.gitignore
vendored
10
.gitignore
vendored
@@ -7,8 +7,11 @@ Borealis-Server.exe
|
|||||||
# Production Deployment Folders
|
# Production Deployment Folders
|
||||||
/Agent/
|
/Agent/
|
||||||
/Server/
|
/Server/
|
||||||
|
/Certificates/
|
||||||
/ElectronApp/
|
/ElectronApp/
|
||||||
/Logs/
|
/Logs/
|
||||||
|
/Temp/
|
||||||
|
database.db
|
||||||
|
|
||||||
# On-the-Fly Downloaded Dependencies
|
# On-the-Fly Downloaded Dependencies
|
||||||
/Dependencies/NodeJS/
|
/Dependencies/NodeJS/
|
||||||
@@ -20,11 +23,4 @@ Borealis-Server.exe
|
|||||||
# Misc Files/Folders
|
# Misc Files/Folders
|
||||||
.vs/s
|
.vs/s
|
||||||
__pycache__
|
__pycache__
|
||||||
/Agent/Python_API_Endpoints/__pycache__/
|
|
||||||
/Update_Staging/
|
/Update_Staging/
|
||||||
agent_settings.json
|
|
||||||
agent_settings_svc.json
|
|
||||||
agent_settings_user.json
|
|
||||||
users.json
|
|
||||||
database.db
|
|
||||||
/Temp/
|
|
||||||
@@ -852,7 +852,7 @@ function InstallOrUpdate-BorealisAgent {
|
|||||||
if (-not (Test-Path $serverUrlPath) -and (Test-Path $oldServerUrlPath)) {
|
if (-not (Test-Path $serverUrlPath) -and (Test-Path $oldServerUrlPath)) {
|
||||||
try { Move-Item -Path $oldServerUrlPath -Destination $serverUrlPath -Force } catch { try { Copy-Item $oldServerUrlPath $serverUrlPath -Force } catch {} }
|
try { Move-Item -Path $oldServerUrlPath -Destination $serverUrlPath -Force } catch { try { Copy-Item $oldServerUrlPath $serverUrlPath -Force } catch {} }
|
||||||
}
|
}
|
||||||
$defaultUrl = 'http://localhost:5000'
|
$defaultUrl = 'https://localhost:5000'
|
||||||
$currentUrl = $defaultUrl
|
$currentUrl = $defaultUrl
|
||||||
if ($existingServerUrl -and $existingServerUrl.Trim()) {
|
if ($existingServerUrl -and $existingServerUrl.Trim()) {
|
||||||
$currentUrl = $existingServerUrl.Trim()
|
$currentUrl = $existingServerUrl.Trim()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import platform
|
|||||||
import stat
|
import stat
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
from pathlib import Path
|
||||||
from typing import List, Optional, Tuple
|
from typing import List, Optional, Tuple
|
||||||
|
|
||||||
import ssl
|
import ssl
|
||||||
@@ -55,6 +56,49 @@ def _restrict_permissions(path: str) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_agent_certificate_dir(settings_dir: str, scope: str) -> str:
|
||||||
|
scope_name = (scope or "CURRENTUSER").strip().upper() or "CURRENTUSER"
|
||||||
|
|
||||||
|
def _as_path(value: Optional[str]) -> Optional[Path]:
|
||||||
|
if not value:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return Path(value).expanduser().resolve()
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
return Path(value).expanduser()
|
||||||
|
except Exception:
|
||||||
|
return Path(value)
|
||||||
|
|
||||||
|
env_agent_root = _as_path(os.environ.get("BOREALIS_AGENT_CERT_ROOT"))
|
||||||
|
env_cert_root = _as_path(os.environ.get("BOREALIS_CERTIFICATES_ROOT")) or _as_path(
|
||||||
|
os.environ.get("BOREALIS_CERT_ROOT")
|
||||||
|
)
|
||||||
|
|
||||||
|
if env_agent_root is not None:
|
||||||
|
base = env_agent_root
|
||||||
|
elif env_cert_root is not None:
|
||||||
|
base = env_cert_root / "Agent"
|
||||||
|
else:
|
||||||
|
settings_path = Path(settings_dir).resolve()
|
||||||
|
try:
|
||||||
|
project_root = settings_path.parents[2]
|
||||||
|
except Exception:
|
||||||
|
project_root = settings_path.parent
|
||||||
|
base = project_root / "Certificates" / "Agent"
|
||||||
|
|
||||||
|
target = base / "Trusted_Server_Cert"
|
||||||
|
if scope_name not in {"SYSTEM", "CURRENTUSER"}:
|
||||||
|
target = target / scope_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
target.mkdir(parents=True, exist_ok=True)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return str(target)
|
||||||
|
|
||||||
|
|
||||||
class _FileLock:
|
class _FileLock:
|
||||||
def __init__(self, path: str) -> None:
|
def __init__(self, path: str) -> None:
|
||||||
self.path = path
|
self.path = path
|
||||||
@@ -226,15 +270,17 @@ class AgentIdentity:
|
|||||||
class AgentKeyStore:
|
class AgentKeyStore:
|
||||||
def __init__(self, settings_dir: str, scope: str = "CURRENTUSER") -> None:
|
def __init__(self, settings_dir: str, scope: str = "CURRENTUSER") -> None:
|
||||||
self.settings_dir = settings_dir
|
self.settings_dir = settings_dir
|
||||||
self.scope_system = scope.upper() == "SYSTEM"
|
self.scope_name = (scope or "CURRENTUSER").strip().upper() or "CURRENTUSER"
|
||||||
|
self.scope_system = self.scope_name == "SYSTEM"
|
||||||
_ensure_dir(self.settings_dir)
|
_ensure_dir(self.settings_dir)
|
||||||
|
self._certificate_dir = _resolve_agent_certificate_dir(self.settings_dir, self.scope_name)
|
||||||
self._private_path = os.path.join(self.settings_dir, "agent_key.ed25519")
|
self._private_path = os.path.join(self.settings_dir, "agent_key.ed25519")
|
||||||
self._public_path = os.path.join(self.settings_dir, "agent_key.pub")
|
self._public_path = os.path.join(self.settings_dir, "agent_key.pub")
|
||||||
self._guid_path = os.path.join(self.settings_dir, "guid.txt")
|
self._guid_path = os.path.join(self.settings_dir, "guid.txt")
|
||||||
self._access_token_path = os.path.join(self.settings_dir, "access.jwt")
|
self._access_token_path = os.path.join(self.settings_dir, "access.jwt")
|
||||||
self._refresh_token_path = os.path.join(self.settings_dir, "refresh.token")
|
self._refresh_token_path = os.path.join(self.settings_dir, "refresh.token")
|
||||||
self._token_meta_path = os.path.join(self.settings_dir, "access.meta.json")
|
self._token_meta_path = os.path.join(self.settings_dir, "access.meta.json")
|
||||||
self._server_certificate_path = os.path.join(self.settings_dir, "server_certificate.pem")
|
self._server_certificate_path = os.path.join(self._certificate_dir, "server_certificate.pem")
|
||||||
self._server_signing_key_path = os.path.join(self.settings_dir, "server_signing_key.pub")
|
self._server_signing_key_path = os.path.join(self.settings_dir, "server_signing_key.pub")
|
||||||
self._identity_lock_path = os.path.join(self.settings_dir, "identity.lock")
|
self._identity_lock_path = os.path.join(self.settings_dir, "identity.lock")
|
||||||
self._installer_cache_path = os.path.join(self.settings_dir, "installer_code.shared.json")
|
self._installer_cache_path = os.path.join(self.settings_dir, "installer_code.shared.json")
|
||||||
|
|||||||
@@ -141,6 +141,18 @@ def register(
|
|||||||
# Another device already claims this hostname; keep the existing
|
# Another device already claims this hostname; keep the existing
|
||||||
# canonical hostname assigned during enrollment to avoid breaking
|
# canonical hostname assigned during enrollment to avoid breaking
|
||||||
# the unique constraint and continue updating the remaining fields.
|
# 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:
|
if "hostname" in updates:
|
||||||
updates.pop("hostname", None)
|
updates.pop("hostname", None)
|
||||||
try:
|
try:
|
||||||
@@ -148,12 +160,23 @@ def register(
|
|||||||
except sqlite3.IntegrityError:
|
except sqlite3.IntegrityError:
|
||||||
raise
|
raise
|
||||||
else:
|
else:
|
||||||
log(
|
try:
|
||||||
"server",
|
current_guid = normalize_guid(ctx.guid)
|
||||||
"heartbeat hostname collision ignored for guid="
|
except Exception:
|
||||||
f"{ctx.guid}",
|
current_guid = ctx.guid
|
||||||
context_label,
|
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:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -19,13 +19,18 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
from cryptography.x509.oid import NameOID
|
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"
|
_CERT_FILE = _CERT_DIR / "borealis-server-cert.pem"
|
||||||
_KEY_FILE = _CERT_DIR / "borealis-server-key.pem"
|
_KEY_FILE = _CERT_DIR / "borealis-server-key.pem"
|
||||||
_BUNDLE_FILE = _CERT_DIR / "borealis-server-bundle.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).
|
# 100-year lifetime (effectively "never" for self-signed deployments).
|
||||||
_CERT_VALIDITY = timedelta(days=365 * 100)
|
_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).
|
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())
|
regenerate = not (_CERT_FILE.exists() and _KEY_FILE.exists())
|
||||||
if not regenerate:
|
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
|
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:
|
def _generate_certificate(common_name: str) -> None:
|
||||||
private_key = ec.generate_private_key(ec.SECP384R1())
|
private_key = ec.generate_private_key(ec.SECP384R1())
|
||||||
public_key = private_key.public_key()
|
public_key = private_key.public_key()
|
||||||
|
|||||||
@@ -10,15 +10,22 @@ from typing import Tuple
|
|||||||
from cryptography.hazmat.primitives import serialization
|
from cryptography.hazmat.primitives import serialization
|
||||||
from cryptography.hazmat.primitives.asymmetric import ed25519
|
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
|
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_KEY_FILE = _KEY_DIR / "borealis-script-ed25519.key"
|
||||||
_SIGNING_PUB_FILE = _KEY_DIR / "borealis-script-ed25519.pub"
|
_SIGNING_PUB_FILE = _KEY_DIR / "borealis-script-ed25519.pub"
|
||||||
_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-script-ed25519.key"
|
_LEGACY_KEY_FILE = runtime_path("keys") / "borealis-script-ed25519.key"
|
||||||
_LEGACY_PUB_FILE = runtime_path("keys") / "borealis-script-ed25519.pub"
|
_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:
|
class ScriptSigner:
|
||||||
@@ -45,7 +52,7 @@ def load_signer() -> ScriptSigner:
|
|||||||
|
|
||||||
|
|
||||||
def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
||||||
ensure_runtime_dir("script_signing_keys")
|
ensure_server_certificates_dir("Code-Signing")
|
||||||
_migrate_legacy_material_if_present()
|
_migrate_legacy_material_if_present()
|
||||||
|
|
||||||
if _SIGNING_KEY_FILE.exists():
|
if _SIGNING_KEY_FILE.exists():
|
||||||
@@ -80,11 +87,30 @@ def _load_or_create() -> ed25519.Ed25519PrivateKey:
|
|||||||
|
|
||||||
|
|
||||||
def _migrate_legacy_material_if_present() -> None:
|
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():
|
if not _LEGACY_KEY_FILE.exists() or _SIGNING_KEY_FILE.exists():
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
ensure_runtime_dir("script_signing_keys")
|
ensure_server_certificates_dir("Code-Signing")
|
||||||
try:
|
try:
|
||||||
_LEGACY_KEY_FILE.replace(_SIGNING_KEY_FILE)
|
_LEGACY_KEY_FILE.replace(_SIGNING_KEY_FILE)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -97,4 +123,3 @@ def _migrate_legacy_material_if_present() -> None:
|
|||||||
_SIGNING_PUB_FILE.write_bytes(_LEGACY_PUB_FILE.read_bytes())
|
_SIGNING_PUB_FILE.write_bytes(_LEGACY_PUB_FILE.read_bytes())
|
||||||
except Exception:
|
except Exception:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -82,13 +82,47 @@ def ensure_runtime_dir(*parts: str) -> Path:
|
|||||||
def certificates_root() -> Path:
|
def certificates_root() -> Path:
|
||||||
"""Base directory for persisted certificate material."""
|
"""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:
|
if env:
|
||||||
env.mkdir(parents=True, exist_ok=True)
|
env.mkdir(parents=True, exist_ok=True)
|
||||||
return env
|
return env
|
||||||
|
|
||||||
root = project_root() / "Certificates"
|
root = project_root() / "Certificates"
|
||||||
root.mkdir(parents=True, exist_ok=True)
|
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
|
return root
|
||||||
|
|
||||||
|
|
||||||
@@ -104,3 +138,31 @@ def ensure_certificates_dir(*parts: str) -> Path:
|
|||||||
path = certificates_path(*parts)
|
path = certificates_path(*parts)
|
||||||
path.mkdir(parents=True, exist_ok=True)
|
path.mkdir(parents=True, exist_ok=True)
|
||||||
return path
|
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
|
||||||
|
|||||||
@@ -50,25 +50,40 @@ else:
|
|||||||
_original_handle_one_request = HttpProtocol.handle_one_request
|
_original_handle_one_request = HttpProtocol.handle_one_request
|
||||||
|
|
||||||
def _quiet_tls_http_mismatch(self): # type: ignore[override]
|
def _quiet_tls_http_mismatch(self): # type: ignore[override]
|
||||||
|
def _close_connection_quietly():
|
||||||
|
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
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return _original_handle_one_request(self)
|
return _original_handle_one_request(self)
|
||||||
except ssl.SSLError as exc: # type: ignore[arg-type]
|
except ssl.SSLError as exc: # type: ignore[arg-type]
|
||||||
reason = getattr(exc, "reason", "")
|
reason = getattr(exc, "reason", "")
|
||||||
reason_text = str(reason).lower() if reason else ""
|
reason_text = str(reason).lower() if reason else ""
|
||||||
message = " ".join(str(arg) for arg in exc.args if arg).lower()
|
message = " ".join(str(arg) for arg in exc.args if arg).lower()
|
||||||
if "http_request" in message or reason_text == "http request":
|
if (
|
||||||
try:
|
"http_request" in message
|
||||||
self.close_connection = True # type: ignore[attr-defined]
|
or reason_text == "http request"
|
||||||
except Exception:
|
or "unknown ca" in message
|
||||||
pass
|
or reason_text == "unknown ca"
|
||||||
try:
|
or "unknown_ca" in message
|
||||||
conn = getattr(self, "socket", None) or getattr(self, "connection", None)
|
):
|
||||||
if conn:
|
_close_connection_quietly()
|
||||||
conn.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return None
|
return None
|
||||||
raise
|
raise
|
||||||
|
except ssl.SSLEOFError:
|
||||||
|
_close_connection_quietly()
|
||||||
|
return None
|
||||||
|
except ConnectionAbortedError:
|
||||||
|
_close_connection_quietly()
|
||||||
|
return None
|
||||||
|
|
||||||
HttpProtocol.handle_one_request = _quiet_tls_http_mismatch # type: ignore[assignment]
|
HttpProtocol.handle_one_request = _quiet_tls_http_mismatch # type: ignore[assignment]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user