Fix: Résolution des erreurs de création de périmètre fonctionnel et de certificats.

Ce commit adresse plusieurs problèmes qui empêchaient la création correcte des périmètres fonctionnels et de leurs certificats intermédiaires associés.

Modifications apportées :

1.  **Scripts Shell (create_root_cert.sh, create_intermediate_cert.sh):**
    *   Correction des chemins de copie pour les fichiers de configuration OpenSSL (`root-openssl.conf`, `intermediate-openssl.conf`) pour utiliser `/opt/scripts/configs/` comme source correcte dans l'environnement d'exécution.
    *   Ajout de `set -e` au début des scripts pour un arrêt immédiat en cas d'erreur.
    *   Modification de `scripts/configs/root-openssl.conf` pour que sa directive `dir` pointe vers `/opt/tls/root`, assurant que la CA Racine trouve correctement ses propres fichiers.
    *   Modification de `scripts/create_intermediate_cert.sh` pour que la directive `dir` dans les fichiers `openssl.cnf` des CA intermédiaires soit dynamiquement ajustée au chemin spécifique du périmètre (ex: `/opt/tls/intermediate/nom_perimetre`).
    *   `create_intermediate_cert.sh` accepte maintenant `ROOT_DOMAIN` comme argument et l'utilise pour rendre uniques les CN des certificats intermédiaires et OCSP (ex: `nom_perimetre.intermediate.example.com`).
    *   Correction des chemins de sortie pour la CRL et les fichiers OCSP (clé privée et CSR) dans `create_intermediate_cert.sh` pour utiliser `$INTERMEDIATE_CA_DIR` comme base.

2.  **Contrôleur PHP (PerimeterController.php):**
    *   Extraction dynamique du `ROOT_DOMAIN` à partir du CN du certificat de la CA Racine existante et passage de cette valeur au script `create_intermediate_cert.sh`.
    *   Utilisation d'une expression régulière plus robuste pour l'extraction du `ROOT_DOMAIN`.
    *   Passage des variables d'environnement `OCSP_URL` (depuis la configuration PHP) et `SAN` (vide) au script `create_intermediate_cert.sh` pour éviter les erreurs de "variable has no value" dans OpenSSL.

3.  **Initialisation (app/public/index.php):**
    *   Ajout de logs de débogage et d'une gestion d'erreur défensive pour s'assurer que `$_SESSION['init_root_domain']` est correctement défini avant d'être utilisé pour créer la CA Racine.

Ces changements combinés résolvent le problème initial de création de certificat intermédiaire et plusieurs autres problèmes découverts, menant à une création réussie des périmètres fonctionnels.
This commit is contained in:
google-labs-jules[bot]
2025-06-15 17:12:51 +00:00
parent 0c2d74a35b
commit 32aced02d6
5 changed files with 98 additions and 11 deletions

View File

@ -81,6 +81,8 @@ if ($userCount === 0 || !$rootCertExists) {
if (isset($_POST['admin_password'], $_POST['root_domain']) && !empty($_POST['admin_password']) && !empty($_POST['root_domain'])) {
$_SESSION['init_admin_password'] = $_POST['admin_password'];
$_SESSION['init_root_domain'] = $_POST['root_domain'];
$logService->log('debug', 'Initialisation - POST data received: admin_password_present=' . !empty($_POST['admin_password']) . ', root_domain=' . ($_POST['root_domain'] ?? 'not_set_or_empty'));
$logService->log('debug', 'Initialisation - Session variables SET: init_admin_password_present=' . !empty($_SESSION['init_admin_password']) . ', init_root_domain=' . ($_SESSION['init_root_domain'] ?? 'not_set_or_empty'));
header('Location: ' . $_SERVER['PHP_SELF']);
exit();
} else {
@ -108,6 +110,25 @@ if ($userCount === 0 || !$rootCertExists) {
if (!$rootCertExists) {
echo "<p>Création du certificat Root CA en cours...</p>";
$logService->log('info', 'Lancement de la création du certificat Root CA pour le domaine: ' . $_SESSION['init_root_domain'], null, $_SERVER['REMOTE_ADDR']);
$logService->log('debug', 'Initialisation - About to call create_root_cert.sh. Value of $_SESSION[\'init_root_domain\']: ' . ($_SESSION['init_root_domain'] ?? 'NOT SET OR EMPTY'));
if (empty($_SESSION['init_root_domain'])) {
$logService->log('error', 'Initialisation - CRITICAL: $_SESSION[\'init_root_domain\'] is empty or not set right before calling create_root_cert.sh. Forcing display of error and form again.');
// Code to re-display form or a clear error message, then exit.
// This is to prevent the script from being called with an empty argument.
echo "<p style='color: red;'><strong>Erreur Critique:</strong> La variable de session pour le domaine racine est vide avant d'appeler le script de création. Veuillez réessayer.</p>";
// Minimal form for resubmission:
echo "<form method='POST' action='" . htmlspecialchars($_SERVER['PHP_SELF']) . "'>";
echo "<div><label for='admin_password'>Mot de passe administrateur initial :</label><input type='password' id='admin_password' name='admin_password' required></div>";
echo "<div><label for='root_domain'>Domaine racine (ROOT_DOMAIN) pour le certificat CA :</label><input type='text' id='root_domain' name='root_domain' required><small>Exemple: exemple.com</small></div>";
echo "<button type='submit'>Configurer</button>";
echo "</form>";
// Optionally, unset session variables to force re-entry of CAS 1.2 logic fully.
unset($_SESSION['init_admin_password']);
unset($_SESSION['init_root_domain']);
exit();
}
// Exécution du script shell de création de certificat root avec le domaine racine
$command = escapeshellcmd(SCRIPTS_PATH . '/create_root_cert.sh ' . escapeshellarg($_SESSION['init_root_domain']));
$output = shell_exec($command . ' 2>&1');

View File

@ -89,6 +89,10 @@ class PerimeterController
$perimeterName = trim($_POST['name'] ?? '');
$ipAddress = $_SERVER['REMOTE_ADDR'];
$userId = $this->authService->getUserId();
// La passphrase est optionnelle pour l'intermédiaire, mais le script attend l'argument.
// Le script create_intermediate_cert.sh a été modifié pour accepter "EMPTY_STRING"
// si aucune passphrase n'est fournie.
$passphrase = trim($_POST['intermediate_passphrase'] ?? ''); // Assumons que cela vient du formulaire
if (empty($perimeterName)) {
$_SESSION['error'] = $this->langService->__('perimeter_create_error_empty_name');
@ -105,14 +109,58 @@ class PerimeterController
exit();
}
// Extraire ROOT_DOMAIN du certificat CA racine
$rootCaCertPath = ROOT_CA_PATH . '/certs/ca.cert.pem';
$rootDomain = null;
if (!file_exists($rootCaCertPath)) {
$this->logService->log('error', "Certificat CA racine non trouvé à: $rootCaCertPath", $userId, $ipAddress);
$_SESSION['error'] = $this->langService->__('perimeter_create_error_root_cert_missing'); // Nouvelle clé de traduction
header('Location: /perimeters/create');
exit();
}
$subjectCommand = "openssl x509 -noout -subject -in " . escapeshellarg($rootCaCertPath);
$subjectLine = shell_exec($subjectCommand);
if ($subjectLine && preg_match('/CN\s*=\s*ca\.([^\/,\s]+)/', $subjectLine, $matches)) {
$rootDomain = $matches[1];
}
if (empty($rootDomain)) {
$this->logService->log('error', "Impossible d'extraire ROOT_DOMAIN du certificat CA racine. Regex: '/CN\s*=\s*ca\.([^\/,\s]+)/' Sujet obtenu: " . ($subjectLine ?: 'vide ou non recupere'), $userId, $ipAddress);
$_SESSION['error'] = $this->langService->__('perimeter_create_error_root_domain'); // Clé de traduction existante ou à ajouter
header('Location: /perimeters/create');
exit();
}
$this->logService->log('info', "ROOT_DOMAIN extrait avec succès: $rootDomain", $userId, $ipAddress);
// Appeler le script shell pour créer le certificat intermédiaire
$command = escapeshellcmd(SCRIPTS_PATH . '/create_intermediate_cert.sh') . ' ' . escapeshellarg($perimeterName);
// Retrieve OCSP_URL from defined constant
$ocspUrl = OCSP_URL; // This constant is defined in app/src/config/app.php
$sanValue = ''; // SAN is not typically needed for an intermediate CA, set to empty
$passphraseArg = !empty($passphrase) ? $passphrase : "EMPTY_STRING";
// Construct the shell command with environment variables
// Note: escapeshellcmd is applied to the script path.
// Environment variable values should also be safe.
// Using escapeshellarg for values assigned to env vars is a good practice.
$scriptPath = SCRIPTS_PATH . '/create_intermediate_cert.sh';
$command = "OCSP_URL=" . escapeshellarg($ocspUrl) . " SAN=" . escapeshellarg($sanValue) . " " .
escapeshellcmd($scriptPath) . ' ' .
escapeshellarg($perimeterName) . ' ' .
escapeshellarg($passphraseArg) . ' ' .
escapeshellarg($rootDomain);
$this->logService->log('info', "Tentative de création du périmètre '$perimeterName' et de son certificat intermédiaire. Commande: '$command'", $userId, $ipAddress);
$output = shell_exec($command . ' 2>&1');
if (strpos($output, "Certificat Intermédiaire CA pour '$perimeterName' créé avec succès") !== false) {
// La condition de succès doit correspondre à la sortie du script.
// Le script create_intermediate_cert.sh se termine par :
// echo "Certificat Intermédiaire CA pour '$FUNCTIONAL_PERIMETER_NAME' créé : $INTERMEDIATE_CERT"
// Nous allons donc chercher une partie de cette chaîne.
if (strpos($output, "Certificat Intermédiaire CA pour '$perimeterName' créé") !== false) {
// Enregistrer le périmètre dans la base de données
$stmt = $this->db->prepare("INSERT INTO functional_perimeters (name, intermediate_cert_name) VALUES (?, ?)");
$intermediateCertFileName = "intermediate.cert.pem"; // Nom générique du fichier pour l'intermédiaire