feat: add auto secure boot to new kernel update
This commit is contained in:
113
lib/secure_boot.py
Normal file
113
lib/secure_boot.py
Normal file
@ -0,0 +1,113 @@
|
||||
"""Intégration Secure Boot pour les transactions qui modifient le démarrage."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Iterable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
SIGNER_PATH = Path("/usr/local/sbin/secureboot-sign-cachyos")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SecureBootResult:
|
||||
success: bool
|
||||
configured: bool
|
||||
error: str | None = None
|
||||
|
||||
|
||||
def boot_artifacts_may_change(package_names: Iterable[str]) -> bool:
|
||||
"""Indique si la transaction peut remplacer un noyau ou un chargeur EFI."""
|
||||
for name in package_names:
|
||||
if name in {"grub", "shim-signed", "systemd"}:
|
||||
return True
|
||||
if name == "linux" or name.startswith("linux-"):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _run(command: list[str], timeout: int = 120) -> subprocess.CompletedProcess:
|
||||
return subprocess.run(
|
||||
command,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
|
||||
def _error_message(result: subprocess.CompletedProcess) -> str:
|
||||
output = (result.stderr or result.stdout).strip()
|
||||
return output[-800:] if output else f"code de sortie {result.returncode}"
|
||||
|
||||
|
||||
def prepare_secure_boot(package_names: Iterable[str]) -> SecureBootResult:
|
||||
"""Garantit que l'ESP est montée avant une transaction sensible.
|
||||
|
||||
L'absence du signer signifie simplement que Secure Boot n'est pas géré par
|
||||
cette installation. En revanche, une configuration présente mais
|
||||
inutilisable bloque la transaction pour ne pas installer un noyau non signé.
|
||||
"""
|
||||
names = list(package_names)
|
||||
if not boot_artifacts_may_change(names) or not SIGNER_PATH.is_file():
|
||||
return SecureBootResult(success=True, configured=False)
|
||||
|
||||
if os.geteuid() != 0:
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error="la préparation Secure Boot doit être exécutée en root",
|
||||
)
|
||||
|
||||
if not os.access(SIGNER_PATH, os.X_OK):
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error=f"le signer Secure Boot n'est pas exécutable : {SIGNER_PATH}",
|
||||
)
|
||||
|
||||
try:
|
||||
result = _run([str(SIGNER_PATH), "--prepare"])
|
||||
except (OSError, subprocess.TimeoutExpired) as error:
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error=f"impossible d'exécuter la pré-vérification : {error}",
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error=f"pré-vérification du signer échouée : {_error_message(result)}",
|
||||
)
|
||||
|
||||
return SecureBootResult(success=True, configured=True)
|
||||
|
||||
|
||||
def refresh_secure_boot(package_names: Iterable[str]) -> SecureBootResult:
|
||||
"""Reconstruit et signe la chaîne CachyOS après la transaction pacman."""
|
||||
names = list(package_names)
|
||||
if not boot_artifacts_may_change(names) or not SIGNER_PATH.is_file():
|
||||
return SecureBootResult(success=True, configured=False)
|
||||
|
||||
try:
|
||||
result = _run([str(SIGNER_PATH)], timeout=180)
|
||||
except (OSError, subprocess.TimeoutExpired) as error:
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error=f"impossible d'exécuter le signer : {error}",
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
return SecureBootResult(
|
||||
success=False,
|
||||
configured=True,
|
||||
error=f"signature Secure Boot échouée : {_error_message(result)}",
|
||||
)
|
||||
|
||||
logger.info("Chaîne de démarrage Secure Boot reconstruite et vérifiée")
|
||||
return SecureBootResult(success=True, configured=True)
|
||||
Reference in New Issue
Block a user