From de80b91b498949952cf3582ab48dc0670f121de5 Mon Sep 17 00:00:00 2001
From: dyzulk <66510723+dyzulk@users.noreply.github.com>
Date: Thu, 8 Jan 2026 08:51:46 +0700
Subject: [PATCH] Refactor: Extract installer logic to CaInstallerService
---
.../Controllers/Api/PublicCaController.php | 109 +---
app/Services/CaInstallerService.php | 525 ++++++++++++++++++
app/Services/OpenSslService.php | 367 +-----------
3 files changed, 561 insertions(+), 440 deletions(-)
create mode 100644 app/Services/CaInstallerService.php
diff --git a/app/Http/Controllers/Api/PublicCaController.php b/app/Http/Controllers/Api/PublicCaController.php
index de4509c..26b3c1c 100644
--- a/app/Http/Controllers/Api/PublicCaController.php
+++ b/app/Http/Controllers/Api/PublicCaController.php
@@ -104,28 +104,15 @@ class PublicCaController extends Controller
return redirect()->away(Storage::disk('r2-public')->url($cert->bat_path));
}
- $store = $cert->ca_type === 'root' ? 'Root' : 'CA';
- $filename = preg_replace('/[^a-zA-Z0-9_-]/', '_', $cert->common_name);
-
- // Convert CRLF to ensure batch file works
- $certContent = str_replace("\n", "\r\n", str_replace("\r\n", "\n", $cert->cert_content));
+ // Fallback: Generate via CaInstallerService
+ $installerService = app(\App\Services\CaInstallerService::class);
+ $content = $installerService->generateWindowsInstaller($cert, false);
+ $filename = 'install-trustlab-' . \Illuminate\Support\Str::slug($cert->common_name) . '.bat';
- $script = "@echo off\r\n";
- $script .= "echo Installing " . $cert->common_name . "...\r\n";
- $script .= "echo Please allow the security prompt to trust this certificate.\r\n";
- $script .= "set \"CERT_FILE=%TEMP%\\" . $filename . ".crt\"\r\n";
- $script .= "((\r\n";
- foreach(explode("\r\n", $certContent) as $line) {
- if(!empty($line)) $script .= "echo " . $line . "\r\n";
- }
- $script .= ")) > \"%CERT_FILE%\"\r\n";
- $script .= "certutil -addstore -f \"" . $store . "\" \"%CERT_FILE%\"\r\n";
- $script .= "del \"%CERT_FILE%\"\r\n";
- $script .= "pause\r\n";
-
- return response($script)
- ->header('Content-Type', 'application/x-bat')
- ->header('Content-Disposition', 'attachment; filename="install-' . $filename . '.bat"');
+ return response($content, 200, [
+ 'Content-Type' => 'application/x-bat',
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ ]);
}
/**
@@ -140,66 +127,16 @@ class PublicCaController extends Controller
if ($cert->mac_path) {
return redirect()->away(Storage::disk('r2-public')->url($cert->mac_path));
}
-
- // Extract Base64 payload
- $pem = $cert->cert_content;
- $lines = explode("\n", trim($pem));
- $payload = '';
- foreach ($lines as $line) {
- if (!str_starts_with($line, '-----')) {
- $payload .= trim($line);
- }
- }
- $uuid = \Illuminate\Support\Str::uuid();
- $identifier = 'com.trustlab.cert.' . $serial;
- $name = $cert->common_name;
-
- $xml = '
-
-
-
- PayloadContent
-
-
- PayloadCertificateFileName
- ' . $name . '.cer
- PayloadContent
-
- ' . $payload . '
-
- PayloadDescription
- Adds ' . $name . ' to Trusted Root Store
- PayloadDisplayName
- ' . $name . '
- PayloadIdentifier
- ' . $identifier . '.cert
- PayloadType
- com.apple.security.pkcs1
- PayloadUUID
- ' . \Illuminate\Support\Str::uuid() . '
- PayloadVersion
- 1
-
-
- PayloadDisplayName
- ' . $name . ' Installer
- PayloadIdentifier
- ' . $identifier . '
- PayloadRemovalDisallowed
-
- PayloadType
- Configuration
- PayloadUUID
- ' . $uuid . '
- PayloadVersion
- 1
-
-';
+ // Fallback: Generate via CaInstallerService
+ $installerService = app(\App\Services\CaInstallerService::class);
+ $content = $installerService->generateMacInstaller($cert);
+ $filename = 'trustlab-' . \Illuminate\Support\Str::slug($cert->common_name) . '.mobileconfig';
- return response($xml)
- ->header('Content-Type', 'application/x-apple-aspen-config')
- ->header('Content-Disposition', 'attachment; filename="' . $name . '.mobileconfig"');
+ return response($content, 200, [
+ 'Content-Type' => 'application/x-apple-aspen-config',
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ ]);
}
/**
@@ -215,12 +152,14 @@ class PublicCaController extends Controller
return redirect()->away(Storage::disk('r2-public')->url($cert->linux_path));
}
- // Fallback or dynamic generation if needed (already in Service)
- $sslService = app(\App\Services\OpenSslService::class);
- $script = $sslService->generateLinuxInstaller($cert);
+ // Fallback: Generate via CaInstallerService
+ $installerService = app(\App\Services\CaInstallerService::class);
+ $content = $installerService->generateLinuxInstaller($cert, false);
+ $filename = 'install-trustlab-' . \Illuminate\Support\Str::slug($cert->common_name) . '.sh';
- return response($script)
- ->header('Content-Type', 'application/x-sh')
- ->header('Content-Disposition', 'attachment; filename="install-' . Str::slug($cert->common_name) . '.sh"');
+ return response($content, 200, [
+ 'Content-Type' => 'application/x-sh',
+ 'Content-Disposition' => 'attachment; filename="' . $filename . '"',
+ ]);
}
}
diff --git a/app/Services/CaInstallerService.php b/app/Services/CaInstallerService.php
new file mode 100644
index 0000000..9d89036
--- /dev/null
+++ b/app/Services/CaInstallerService.php
@@ -0,0 +1,525 @@
+common_name);
+ if ($isArchive) {
+ $cdnUrl = Storage::disk('r2-public')->url("ca/archives/{$cert->uuid}/{$slug}.crt");
+ } else {
+ $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
+ }
+
+ $typeLabel = $cert->ca_type === 'root' ? 'Root' : 'Intermediate';
+ $store = $cert->ca_type === 'root' ? 'Root' : 'CA';
+ $cleanName = $cert->common_name;
+
+ // Hybrid Batch + PowerShell script for rich UI
+ return "@echo off\r\n" .
+ "setlocal\r\n" .
+ "title TrustLab CA Installer\r\n" .
+ "call :printHeader\r\n" .
+ "\r\n" .
+ "echo.\r\n" .
+ "call :printInfo \"Initiating installation for: {$cleanName} ({$typeLabel})\"\r\n" .
+ "\r\n" .
+ "set \"TEMP_CERT=%TEMP%\\trustlab-ca-{$cert->uuid}.crt\"\r\n" .
+ "\r\n" .
+ "call :printAction \"Downloading CA certificate...\"\r\n" .
+ "powershell -Command \"Invoke-WebRequest -Uri '{$cdnUrl}' -OutFile '%TEMP_CERT%'\"\r\n" .
+ "if %ERRORLEVEL% NEQ 0 (\r\n" .
+ " call :printError \"Failed to download certificate.\"\r\n" .
+ " pause\r\n" .
+ " exit /b 1\r\n" .
+ ")\r\n" .
+ "call :printSuccess \"Download complete.\"\r\n" .
+ "\r\n" .
+ "call :printAction \"Installing to Windows Certificate Store ({$store})...\"\r\n" .
+ "certutil -addstore -f \"{$store}\" \"%TEMP_CERT%\" >nul 2>&1\r\n" .
+ "if %ERRORLEVEL% NEQ 0 (\r\n" .
+ " call :printError \"Failed to install certificate to store.\"\r\n" .
+ " del \"%TEMP_CERT%\"\r\n" .
+ " pause\r\n" .
+ " exit /b 1\r\n" .
+ ")\r\n" .
+ "call :printSuccess \"Certificate installed successfully!\"\r\n" .
+ "\r\n" .
+ "del \"%TEMP_CERT%\"\r\n" .
+ "echo.\r\n" .
+ "call :printInfo \"Press any key to close...\"\r\n" .
+ "pause >nul\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printHeader\r\n" .
+ "cls\r\n" .
+ "powershell -Command \"Write-Host ' _______ _____ _ _ _____ _______ _ _______ ______ ' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' |__ __|| __ \| | | |/ ____||__ __|| | |__ __|| _ |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | |__) || | | || (___ | | | | | | | |_) |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | _ / | | | | \___ \ | | | | | | | _ < ' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | | \ \ | |__| | ____) | | | | |____ | | | |_) |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' |_| |_| \_\| \____/ |_____/ |_| |______| |_| |______|' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' '\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printInfo\r\n" .
+ "powershell -Command \"Write-Host ' [ INFO ] %~1' -ForegroundColor Cyan\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printAction\r\n" .
+ "powershell -Command \"Write-Host ' [ .... ] %~1' -ForegroundColor Yellow\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printSuccess\r\n" .
+ "powershell -Command \"Write-Host ' [ OK ] %~1' -ForegroundColor Green\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printError\r\n" .
+ "powershell -Command \"Write-Host ' [ FAIL ] %~1' -ForegroundColor Red\"\r\n" .
+ "exit /b\r\n";
+ }
+
+ /**
+ * Generate macOS Configuration Profile (.mobileconfig)
+ */
+ public function generateMacInstaller(CaCertificate $cert): string
+ {
+ $certBase64 = base64_encode($cert->cert_content);
+ $payloadId = "com.trustlab.ca." . Str::slug($cert->common_name);
+ $uuid1 = Str::uuid()->toString();
+ $uuid2 = Str::uuid()->toString();
+
+ // Root CAs use 'com.apple.security.root', Intermediate CAs use 'com.apple.security.pkcs1' (intermediate)
+ $payloadType = $cert->ca_type === 'root' ? 'com.apple.security.root' : 'com.apple.security.pkcs1';
+
+ return "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ " PayloadContent\n" .
+ " \n" .
+ " \n" .
+ " PayloadCertificateFileName\n" .
+ " {$cert->common_name}.crt\n" .
+ " PayloadContent\n" .
+ " {$certBase64}\n" .
+ " PayloadDescription\n" .
+ " TrustLab CA Certificate\n" .
+ " PayloadDisplayName\n" .
+ " {$cert->common_name}\n" .
+ " PayloadIdentifier\n" .
+ " {$payloadId}.cert\n" .
+ " PayloadType\n" .
+ " {$payloadType}\n" .
+ " PayloadUUID\n" .
+ " {$uuid2}\n" .
+ " PayloadVersion\n" .
+ " 1\n" .
+ " \n" .
+ " \n" .
+ " PayloadDescription\n" .
+ " TrustLab CA Installation\n" .
+ " PayloadDisplayName\n" .
+ " TrustLab CA: {$cert->common_name}\n" .
+ " PayloadIdentifier\n" .
+ " {$payloadId}\n" .
+ " PayloadRemovalDisallowed\n" .
+ " \n" .
+ " PayloadType\n" .
+ " Configuration\n" .
+ " PayloadUUID\n" .
+ " {$uuid1}\n" .
+ " PayloadVersion\n" .
+ " 1\n" .
+ "\n" .
+ "";
+ }
+
+ /**
+ * Generate Linux Installer (.sh) with Proxmox-style Aesthetics
+ */
+ public function generateLinuxInstaller(CaCertificate $cert, bool $isArchive = false): string
+ {
+ $slug = Str::slug($cert->common_name);
+ if ($isArchive) {
+ $cdnUrl = Storage::disk('r2-public')->url("ca/archives/{$cert->uuid}/{$slug}.crt");
+ } else {
+ $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
+ }
+
+ $filename = "trustlab-" . $slug . ".crt";
+
+ return $this->getLinuxHeader() .
+ "header_info \"Installing CA: {$cert->common_name}\"\n" .
+ "\n" .
+ "check_root\n" .
+ "\n" .
+ "TEMP_CERT=\"/tmp/trustlab-{$cert->uuid}.crt\"\n" .
+ "\n" .
+ "msg_info \"Downloading certificate...\"\n" .
+ "if curl -sL \"{$cdnUrl}\" -o \"\$TEMP_CERT\"; then\n" .
+ " msg_ok \"Certificate downloaded.\"\n" .
+ "else\n" .
+ " msg_err \"Failed to download certificate from CDN.\"\n" .
+ " exit 1\n" .
+ "fi\n" .
+ "\n" .
+ "msg_info \"Detecting OS and checking ca-certificates package...\"\n" .
+ "if [ -f /etc/debian_version ]; then\n" .
+ " apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /usr/local/share/ca-certificates\n" .
+ " TARGET_DIR=\"/usr/local/share/ca-certificates\"\n" .
+ " UPDATE_CMD=\"update-ca-certificates\"\n" .
+ "elif [ -f /etc/redhat-release ]; then\n" .
+ " yum install -y -q ca-certificates >/dev/null 2>&1 || dnf install -y -q ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /etc/pki/ca-trust/source/anchors\n" .
+ " TARGET_DIR=\"/etc/pki/ca-trust/source/anchors\"\n" .
+ " UPDATE_CMD=\"update-ca-trust extract\"\n" .
+ "elif [ -f /etc/arch-release ]; then\n" .
+ " pacman -Sy --noconfirm -q ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /etc/ca-certificates/trust-source/anchors\n" .
+ " TARGET_DIR=\"/etc/ca-certificates/trust-source/anchors\"\n" .
+ " UPDATE_CMD=\"trust extract-compat\"\n" .
+ "else\n" .
+ " msg_err \"Unsupported Linux distribution.\"\n" .
+ " exit 1\n" .
+ "fi\n" .
+ "\n" .
+ "msg_info \"Installing certificate to \$TARGET_DIR...\"\n" .
+ "cp \"\$TEMP_CERT\" \"\$TARGET_DIR/{$filename}\"\n" .
+ "\n" .
+ "msg_info \"Updating certificate store...\"\n" .
+ "if \$UPDATE_CMD >/dev/null 2>&1; then\n" .
+ " msg_ok \"Store updated successfully.\"\n" .
+ "else\n" .
+ " msg_err \"Failed to update certificate store.\"\n" .
+ " exit 1\n" .
+ "fi\n" .
+ "\n" .
+ "rm \"\$TEMP_CERT\"\n" .
+ "echo -e \"\n${GN} Installation Complete! ${CL}\"\n" .
+ "echo -e \"${BL} Verify with: ${CL}ls \$TARGET_DIR/trustlab-*\"\n";
+ }
+
+ /**
+ * Common Linux Bash Header with Colors & Helpers
+ */
+ private function getLinuxHeader(): string
+ {
+ return "#!/bin/bash\n" .
+ "# TrustLab CA Installer\n" .
+ "# Generated via CaInstallerService\n" .
+ "\n" .
+ "set -e\n" .
+ "\n" .
+ "YW=$(echo \"\\033[33m\")\n" .
+ "BL=$(echo \"\\033[36m\")\n" .
+ "RD=$(echo \"\\033[01;31m\")\n" .
+ "BGN=$(echo \"\\033[4;32m\")\n" .
+ "GN=$(echo \"\\033[1;92m\")\n" .
+ "DGN=$(echo \"\\033[32m\")\n" .
+ "CL=$(echo \"\\033[m\")\n" .
+ "CM=\"${GN}✓${CL}\"\n" .
+ "CROSS=\"${RD}✗${CL}\"\n" .
+ "BFR=\"\\\\r\\\\033[K\"\n" .
+ "HOLD=\"-\"\n" .
+ "\n" .
+ "header_info() {\n" .
+ " clear\n" .
+ " cat << \"EOF\"\n" .
+ "${BL}\n" .
+ " _______ _____ _ _ _____ _______ _ _______ ______ \n" .
+ " |__ __|| __ \| | | |/ ____||__ __|| | |__ __|| _ |\n" .
+ " | | | |__) || | | || (___ | | | | | | | |_) |\n" .
+ " | | | _ / | | | | \___ \ | | | | | | | _ < \n" .
+ " | | | | \ \ | |__| | ____) | | | | |____ | | | |_) |\n" .
+ " |_| |_| \_\| \____/ |_____/ |_| |______| |_| |______|${CL}\n" .
+ "\n" .
+ "EOF\n" .
+ "}\n" .
+ "\n" .
+ "msg_info() {\n" .
+ " local msg=\"$1\"\n" .
+ " echo -ne \" ${BL}[ INFO ]${CL} ${msg}...\"\n" .
+ "}\n" .
+ "\n" .
+ "msg_ok() {\n" .
+ " local msg=\"$1\"\n" .
+ " echo -e \"${BFR} ${GN}[ OK ]${CL} ${msg}\"\n" .
+ "}\n" .
+ "\n" .
+ "msg_err() {\n" .
+ " local msg=\"$1\"\n" .
+ " echo -e \"${BFR} ${RD}[ FAIL ]${CL} ${msg}\"\n" .
+ "}\n" .
+ "\n" .
+ "check_root() {\n" .
+ " if [ \"$(id -u)\" -ne 0 ]; then\n" .
+ " msg_err \"Please run as root (sudo).\"\n" .
+ " exit 1\n" .
+ " fi\n" .
+ "}\n" .
+ "\n";
+ }
+
+ /**
+ * Upload individual installers (SH, BAT, MAC) to CDN.
+ */
+ public function uploadIndividualInstallersOnly(CaCertificate $cert, string $mode = 'both')
+ {
+ $slug = Str::slug($cert->common_name);
+ $cacheControl = 'no-cache, no-store, must-revalidate';
+
+ $syncs = [];
+ if ($mode === 'archive' || $mode === 'both') {
+ $syncs[] = ['base' => "ca/archives/{$cert->uuid}/installers/trustlab-{$slug}", 'isArchive' => true];
+ }
+ if ($mode === 'latest' || $mode === 'both') {
+ $syncs[] = ['base' => "ca/installers/trustlab-{$slug}", 'isArchive' => false];
+ }
+
+ foreach ($syncs as $sync) {
+ $batPath = $sync['base'] . '.bat';
+ $macPath = $sync['base'] . '.mobileconfig';
+ $linuxPath = $sync['base'] . '.sh';
+
+ // 3. Generate and Upload Windows Installer (.bat)
+ $batContent = $this->generateWindowsInstaller($cert, $sync['isArchive']);
+ Storage::disk('r2-public')->put($batPath, $batContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'text/plain',
+ 'CacheControl' => $cacheControl
+ ]);
+
+ // 4. Generate and Upload macOS Profile (.mobileconfig)
+ $macContent = $this->generateMacInstaller($cert); // macOS profiles are self-contained
+ Storage::disk('r2-public')->put($macPath, $macContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'application/x-apple-aspen-config',
+ 'CacheControl' => $cacheControl
+ ]);
+
+ // 5. Generate and Upload Linux Script (.sh)
+ $linuxContent = $this->generateLinuxInstaller($cert, $sync['isArchive']);
+ Storage::disk('r2-public')->put($linuxPath, $linuxContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'text/plain',
+ 'CacheControl' => $cacheControl
+ ]);
+ }
+
+ $cert->update([
+ 'bat_path' => "ca/installers/trustlab-{$slug}.bat",
+ 'mac_path' => "ca/installers/trustlab-{$slug}.mobileconfig",
+ 'linux_path' => "ca/installers/trustlab-{$slug}.sh",
+ 'last_synced_at' => now()
+ ]);
+
+ return true;
+ }
+
+ /**
+ * Generate Global Bundles (Installer Sapujagat)
+ */
+ public function syncAllBundles()
+ {
+ $certificates = CaCertificate::all();
+ if ($certificates->isEmpty()) return false;
+
+ $cacheControl = 'no-cache, no-store, must-revalidate';
+
+ // 1. Linux Bundle (.sh)
+ // Note: Using the same Proxmox-style header
+ $now = now()->format('Y-m-d H:i:s');
+
+ $shContent = $this->getLinuxHeader() .
+ "header_info \"Bundle Installer (All CAs)\"\n" .
+ "\n" .
+ "check_root\n" .
+ "\n" .
+ "msg_info \"Detecting OS and checking ca-certificates package...\"\n" .
+ "if [ -f /etc/debian_version ]; then\n" .
+ " apt-get update -qq >/dev/null 2>&1 && apt-get install -y -qq ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /usr/local/share/ca-certificates\n" .
+ " TARGET_DIR=\"/usr/local/share/ca-certificates\"\n" .
+ " UPDATE_CMD=\"update-ca-certificates\"\n" .
+ "elif [ -f /etc/redhat-release ]; then\n" .
+ " yum install -y -q ca-certificates >/dev/null 2>&1 || dnf install -y -q ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /etc/pki/ca-trust/source/anchors\n" .
+ " TARGET_DIR=\"/etc/pki/ca-trust/source/anchors\"\n" .
+ " UPDATE_CMD=\"update-ca-trust extract\"\n" .
+ "elif [ -f /etc/arch-release ]; then\n" .
+ " pacman -Sy --noconfirm -q ca-certificates >/dev/null 2>&1\n" .
+ " mkdir -p /etc/ca-certificates/trust-source/anchors\n" .
+ " TARGET_DIR=\"/etc/ca-certificates/trust-source/anchors\"\n" .
+ " UPDATE_CMD=\"trust extract-compat\"\n" .
+ "else\n" .
+ " msg_err \"Unsupported Linux distribution.\"\n" .
+ " exit 1\n" .
+ "fi\n" .
+ "\n";
+
+ // Loop add certificate downloads to bundle
+ foreach ($certificates as $cert) {
+ $slug = Str::slug($cert->common_name);
+ // Use public URL for public accessibility
+ $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
+ $filename = "trustlab-" . $slug . ".crt";
+
+ $shContent .= "msg_info \"Processing: {$cert->common_name}\"\n";
+ $shContent .= "curl -sL \"{$cdnUrl}\" -o \"\$TARGET_DIR/{$filename}\"\n";
+ }
+
+ $shContent .= "\nmsg_info \"Updating certificate store...\"\n" .
+ "if \$UPDATE_CMD >/dev/null 2>&1; then\n" .
+ " msg_ok \"All certificates installed & store updated.\"\n" .
+ "else\n" .
+ " msg_err \"Failed to update certificate store.\"\n" .
+ " exit 1\n" .
+ "fi\n" .
+ "\n" .
+ "echo -e \"\n${GN} Complete! Installed all trustlab certs.${CL}\"\n";
+
+
+ Storage::disk('r2-public')->put('ca/bundles/trustlab-all.sh', $shContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'text/plain',
+ 'CacheControl' => $cacheControl
+ ]);
+
+ // 2. Windows Bundle (.bat)
+ // Hybrid Batch + PowerShell script for rich UI
+ $batContent = "@echo off\r\n" .
+ "setlocal\r\n" .
+ "title TrustLab All-in-One Installer\r\n" .
+ "call :printHeader\r\n" .
+ "\r\n" .
+ "echo.\r\n" .
+ "call :printInfo \"Starting Bundle Installation...\"\r\n" .
+ "\r\n";
+
+ foreach ($certificates as $cert) {
+ $slug = Str::slug($cert->common_name);
+ $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
+ $store = $cert->ca_type === 'root' ? 'Root' : 'CA';
+
+ $batContent .= "set \"TEMP_CERT=%TEMP%\\trustlab-{$slug}.crt\"\r\n" .
+ "call :printAction \"Installing {$cert->common_name}...\"\r\n" .
+ "powershell -Command \"Invoke-WebRequest -Uri '{$cdnUrl}' -OutFile '%TEMP_CERT%'\"\r\n" .
+ "certutil -addstore -f \"{$store}\" \"%TEMP_CERT%\" >nul 2>&1\r\n" .
+ "del \"%TEMP_CERT%\"\r\n";
+ }
+
+ $batContent .= "\r\n" .
+ "call :printSuccess \"All certificates processed.\"\r\n" .
+ "echo.\r\n" .
+ "call :printInfo \"Press any key to close...\"\r\n" .
+ "pause >nul\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printHeader\r\n" .
+ "cls\r\n" .
+ "powershell -Command \"Write-Host ' _______ _____ _ _ _____ _______ _ _______ ______ ' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' |__ __|| __ \| | | |/ ____||__ __|| | |__ __|| _ |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | |__) || | | || (___ | | | | | | | |_) |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | _ / | | | | \___ \ | | | | | | | _ < ' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' | | | | \ \ | |__| | ____) | | | | |____ | | | |_) |' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' |_| |_| \_\| \____/ |_____/ |_| |______| |_| |______|' -ForegroundColor Cyan\"\r\n" .
+ "powershell -Command \"Write-Host ' '\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printInfo\r\n" .
+ "powershell -Command \"Write-Host ' [ INFO ] %~1' -ForegroundColor Cyan\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printAction\r\n" .
+ "powershell -Command \"Write-Host ' [ .... ] %~1' -ForegroundColor Yellow\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printSuccess\r\n" .
+ "powershell -Command \"Write-Host ' [ OK ] %~1' -ForegroundColor Green\"\r\n" .
+ "exit /b\r\n" .
+ "\r\n" .
+ ":printError\r\n" .
+ "powershell -Command \"Write-Host ' [ FAIL ] %~1' -ForegroundColor Red\"\r\n" .
+ "exit /b\r\n";
+
+ Storage::disk('r2-public')->put('ca/bundles/trustlab-all.bat', $batContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'text/plain',
+ 'CacheControl' => $cacheControl
+ ]);
+
+ // 3. MacOS Bundle (Config Profile - logic kept as is)
+ $uuid1 = Str::uuid()->toString();
+ $payloadContent = "";
+
+ foreach ($certificates as $cert) {
+ $certBase64 = base64_encode($cert->cert_content);
+ $uuidSub = Str::uuid()->toString();
+ $payloadType = $cert->ca_type === 'root' ? 'com.apple.security.root' : 'com.apple.security.pkcs1';
+
+ $payloadContent .= " \n" .
+ " PayloadCertificateFileName\n" .
+ " {$cert->common_name}.crt\n" .
+ " PayloadContent\n" .
+ " {$certBase64}\n" .
+ " PayloadDescription\n" .
+ " TrustLab CA Certificate\n" .
+ " PayloadDisplayName\n" .
+ " {$cert->common_name}\n" .
+ " PayloadIdentifier\n" .
+ " com.trustlab.bundle.{$cert->uuid}\n" .
+ " PayloadType\n" .
+ " {$payloadType}\n" .
+ " PayloadUUID\n" .
+ " {$uuidSub}\n" .
+ " PayloadVersion\n" .
+ " 1\n" .
+ " \n";
+ }
+
+ $macContent = "\n" .
+ "\n" .
+ "\n" .
+ "\n" .
+ " PayloadContent\n" .
+ " \n" . $payloadContent . " \n" .
+ " PayloadDescription\n" .
+ " TrustLab All-in-One CA Bundle\n" .
+ " PayloadDisplayName\n" .
+ " TrustLab CA Bundle\n" .
+ " PayloadIdentifier\n" .
+ " com.trustlab.ca.bundle\n" .
+ " PayloadRemovalDisallowed\n" .
+ " \n" .
+ " PayloadType\n" .
+ " Configuration\n" .
+ " PayloadUUID\n" .
+ " {$uuid1}\n" .
+ " PayloadVersion\n" .
+ " 1\n" .
+ "\n" .
+ "";
+
+ Storage::disk('r2-public')->delete('ca/bundles/trustlab-all.mobileconfig');
+ Storage::disk('r2-public')->put('ca/bundles/trustlab-all.mobileconfig', $macContent, [
+ 'visibility' => 'public',
+ 'ContentType' => 'application/x-apple-aspen-config',
+ 'CacheControl' => $cacheControl
+ ]);
+
+ return true;
+ }
+}
diff --git a/app/Services/OpenSslService.php b/app/Services/OpenSslService.php
index 96fda63..3036673 100644
--- a/app/Services/OpenSslService.php
+++ b/app/Services/OpenSslService.php
@@ -508,148 +508,6 @@ class OpenSslService
return $newCert;
}
- /**
- * Generate Windows Installer (.bat)
- */
- public function generateWindowsInstaller(CaCertificate $cert, bool $isArchive = false): string
- {
- $slug = Str::slug($cert->common_name);
- if ($isArchive) {
- $cdnUrl = Storage::disk('r2-public')->url("ca/archives/{$cert->uuid}/{$slug}.crt");
- } else {
- $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
- }
-
- $typeLabel = $cert->ca_type === 'root' ? 'Root' : 'Intermediate';
- $store = $cert->ca_type === 'root' ? 'Root' : 'CA';
-
- return "@echo off\n" .
- "echo TrustLab - Installing {$typeLabel} CA Certificate: {$cert->common_name}\n" .
- "set \"TEMP_CERT=%TEMP%\\trustlab-ca-{$cert->uuid}.crt\"\n" .
- "echo Downloading certificate...\n" .
- "curl -L --progress-bar \"{$cdnUrl}\" -o \"%TEMP_CERT%\"\n" .
- "if %ERRORLEVEL% NEQ 0 (\n" .
- " echo Error: Failed to download certificate.\n" .
- " pause\n" .
- " exit /b 1\n" .
- ")\n" .
- "echo Installing to {$store} store...\n" .
- "certutil -addstore -f \"{$store}\" \"%TEMP_CERT%\"\n" .
- "del \"%TEMP_CERT%\"\n" .
- "echo Installation Complete.\n" .
- "pause";
- }
-
- /**
- * Generate macOS Configuration Profile (.mobileconfig)
- */
- public function generateMacInstaller(CaCertificate $cert): string
- {
- $certBase64 = base64_encode($cert->cert_content);
- $payloadId = "com.trustlab.ca." . Str::slug($cert->common_name);
- $uuid1 = Str::uuid()->toString();
- $uuid2 = Str::uuid()->toString();
-
- // Root CAs use 'com.apple.security.root', Intermediate CAs use 'com.apple.security.pkcs1' (intermediate)
- $payloadType = $cert->ca_type === 'root' ? 'com.apple.security.root' : 'com.apple.security.pkcs1';
-
- return "\n" .
- "\n" .
- "\n" .
- "\n" .
- " PayloadContent\n" .
- " \n" .
- " \n" .
- " PayloadCertificateFileName\n" .
- " {$cert->common_name}.crt\n" .
- " PayloadContent\n" .
- " {$certBase64}\n" .
- " PayloadDescription\n" .
- " TrustLab CA Certificate\n" .
- " PayloadDisplayName\n" .
- " {$cert->common_name}\n" .
- " PayloadIdentifier\n" .
- " {$payloadId}.cert\n" .
- " PayloadType\n" .
- " {$payloadType}\n" .
- " PayloadUUID\n" .
- " {$uuid2}\n" .
- " PayloadVersion\n" .
- " 1\n" .
- " \n" .
- " \n" .
- " PayloadDescription\n" .
- " TrustLab CA Installation\n" .
- " PayloadDisplayName\n" .
- " TrustLab CA: {$cert->common_name}\n" .
- " PayloadIdentifier\n" .
- " {$payloadId}\n" .
- " PayloadRemovalDisallowed\n" .
- " \n" .
- " PayloadType\n" .
- " Configuration\n" .
- " PayloadUUID\n" .
- " {$uuid1}\n" .
- " PayloadVersion\n" .
- " 1\n" .
- "\n" .
- "";
- }
-
- /**
- * Generate Linux Installer (.sh)
- */
- public function generateLinuxInstaller(CaCertificate $cert, bool $isArchive = false): string
- {
- $slug = Str::slug($cert->common_name);
- if ($isArchive) {
- $cdnUrl = Storage::disk('r2-public')->url("ca/archives/{$cert->uuid}/{$slug}.crt");
- } else {
- $cdnUrl = Storage::disk('r2-public')->url("ca/{$slug}.crt");
- }
-
- $filename = "trustlab-" . $slug . ".crt";
-
- return "#!/bin/bash\n" .
- "echo \"TrustLab - Installing CA Certificate: {$cert->common_name}\"\n" .
- "if [ \"\$EUID\" -ne 0 ]; then echo \"Please run as root (sudo)\"; exit 1; fi\n" .
- "TEMP_CERT=\"/tmp/trustlab-{$cert->uuid}.crt\"\n" .
- "echo \"Downloading certificate...\"\n" .
- "curl -L --progress-bar \"{$cdnUrl}\" -o \"\$TEMP_CERT\"\n" .
- "if [ ! -f \"\$TEMP_CERT\" ]; then echo \"Failed to download cert\"; exit 1; fi\n\n" .
- "echo \"Checking and installing ca-certificates package...\"\n" .
- "if [ -d /etc/debian_version ]; then\n" .
- " apt-get update -q && apt-get install -y -q ca-certificates\n" .
- " mkdir -p /usr/local/share/ca-certificates\n" .
- "elif [ -f /etc/redhat-release ]; then\n" .
- " yum install -y -q ca-certificates || dnf install -y -q ca-certificates\n" .
- " mkdir -p /etc/pki/ca-trust/source/anchors\n" .
- "elif [ -f /etc/arch-release ]; then\n" .
- " pacman -Sy --noconfirm -q ca-certificates\n" .
- " mkdir -p /etc/ca-certificates/trust-source/anchors\n" .
- "fi\n\n" .
- "# Detection based on directories\n" .
- "if [ -d /usr/local/share/ca-certificates ]; then\n" .
- " cp \"\$TEMP_CERT\" \"/usr/local/share/ca-certificates/{$filename}\"\n" .
- " update-ca-certificates\n" .
- "# RHEL/CentOS/Fedora\n" .
- "elif [ -d /etc/pki/ca-trust/source/anchors ]; then\n" .
- " cp \"\$TEMP_CERT\" \"/etc/pki/ca-trust/source/anchors/{$filename}\"\n" .
- " update-ca-trust extract\n" .
- "# Arch Linux\n" .
- "elif [ -d /etc/ca-certificates/trust-source/anchors ]; then\n" .
- " cp \"\$TEMP_CERT\" \"/etc/ca-certificates/trust-source/anchors/{$filename}\"\n" .
- " trust extract-compat\n" .
- "else\n" .
- " echo \"Unsupported Linux distribution for automatic install after package check.\"\n" .
- " echo \"Please manually install \$TEMP_CERT\"\n" .
- " exit 1\n" .
- "fi\n" .
- "rm \"\$TEMP_CERT\"\n" .
- "echo \"Installation Complete.\"\n" .
- "echo \"To verify, you can check: ls /usr/local/share/ca-certificates/trustlab-*\"\n";
- }
-
/**
* Upload only PEM/DER (The CRT files) to CDN.
*/
@@ -703,62 +561,6 @@ class OpenSslService
return true;
}
- /**
- * Upload individual installers (SH, BAT, MAC) to CDN.
- */
- public function uploadIndividualInstallersOnly(CaCertificate $cert, string $mode = 'both')
- {
- $slug = Str::slug($cert->common_name);
- $cacheControl = 'no-cache, no-store, must-revalidate';
-
- $syncs = [];
- if ($mode === 'archive' || $mode === 'both') {
- $syncs[] = ['base' => "ca/archives/{$cert->uuid}/installers/trustlab-{$slug}", 'isArchive' => true];
- }
- if ($mode === 'latest' || $mode === 'both') {
- $syncs[] = ['base' => "ca/installers/trustlab-{$slug}", 'isArchive' => false];
- }
-
- foreach ($syncs as $sync) {
- $batPath = $sync['base'] . '.bat';
- $macPath = $sync['base'] . '.mobileconfig';
- $linuxPath = $sync['base'] . '.sh';
-
- // 3. Generate and Upload Windows Installer (.bat)
- $batContent = $this->generateWindowsInstaller($cert, $sync['isArchive']);
- Storage::disk('r2-public')->put($batPath, $batContent, [
- 'visibility' => 'public',
- 'ContentType' => 'text/plain',
- 'CacheControl' => $cacheControl
- ]);
-
- // 4. Generate and Upload macOS Profile (.mobileconfig)
- $macContent = $this->generateMacInstaller($cert); // macOS profiles are self-contained
- Storage::disk('r2-public')->put($macPath, $macContent, [
- 'visibility' => 'public',
- 'ContentType' => 'application/x-apple-aspen-config',
- 'CacheControl' => $cacheControl
- ]);
-
- // 5. Generate and Upload Linux Script (.sh)
- $linuxContent = $this->generateLinuxInstaller($cert, $sync['isArchive']);
- Storage::disk('r2-public')->put($linuxPath, $linuxContent, [
- 'visibility' => 'public',
- 'ContentType' => 'text/plain',
- 'CacheControl' => $cacheControl
- ]);
- }
-
- $cert->update([
- 'bat_path' => "ca/installers/trustlab-{$slug}.bat",
- 'mac_path' => "ca/installers/trustlab-{$slug}.mobileconfig",
- 'linux_path' => "ca/installers/trustlab-{$slug}.sh",
- 'last_synced_at' => now()
- ]);
-
- return true;
- }
-
/**
* Promote an archived certificate version to 'Latest' (public root)
*/
@@ -766,167 +568,17 @@ class OpenSslService
{
// Simply re-sync this specific certificate version as 'latest'
$this->uploadPublicCertsOnly($cert, 'latest');
- $this->uploadIndividualInstallersOnly($cert, 'latest');
+
+ // Delegate installer uploads to CaInstallerService
+ $installerService = app(\App\Services\CaInstallerService::class);
+ $installerService->uploadIndividualInstallersOnly($cert, 'latest');
// Also sync all bundles to ensure global installers are updated with this promoted version
- $this->syncAllBundles();
+ $installerService->syncAllBundles();
return true;
}
- /**
- * Generate Global Bundles (Installer Sapujagat)
- */
- public function syncAllBundles()
- {
- $certificates = CaCertificate::all();
- if ($certificates->isEmpty()) return false;
-
- $cacheControl = 'no-cache, no-store, must-revalidate';
-
- // 1. Linux Bundle (.sh)
- $now = now()->format('Y-m-d H:i:s');
- $shContent = "#!/bin/bash\n" .
- "# Generated at: {$now}\n" .
- "echo \"TrustLab - Installing all CA Certificates...\"\n" .
- "if [ \"\$EUID\" -ne 0 ]; then echo \"Please run as root (sudo)\"; exit 1; fi\n\n" .
- "echo \"Checking and installing ca-certificates package... (Please wait)\"\n" .
- "if [ -d /etc/debian_version ]; then\n" .
- " apt-get update -q && apt-get install -y -q ca-certificates\n" .
- " mkdir -p /usr/local/share/ca-certificates\n" .
- "elif [ -f /etc/redhat-release ]; then\n" .
- " yum install -y -q ca-certificates || dnf install -y -q ca-certificates\n" .
- " mkdir -p /etc/pki/ca-trust/source/anchors\n" .
- "elif [ -f /etc/arch-release ]; then\n" .
- " pacman -Sy --noconfirm -q ca-certificates\n" .
- " mkdir -p /etc/ca-certificates/trust-source/anchors\n" .
- "fi\n\n" .
- "# OS Detection after package check\n" .
- "TARGET_DIR=\"\"\n" .
- "UPDATE_CMD=\"\"\n\n" .
- "if [ -d /usr/local/share/ca-certificates ]; then\n" .
- " TARGET_DIR=\"/usr/local/share/ca-certificates\"\n" .
- " UPDATE_CMD=\"update-ca-certificates\"\n" .
- "elif [ -d /etc/pki/ca-trust/source/anchors ]; then\n" .
- " TARGET_DIR=\"/etc/pki/ca-trust/source/anchors\"\n" .
- " UPDATE_CMD=\"update-ca-trust extract\"\n" .
- "elif [ -d /etc/ca-certificates/trust-source/anchors ]; then\n" .
- " TARGET_DIR=\"/etc/ca-certificates/trust-source/anchors\"\n" .
- " UPDATE_CMD=\"trust extract-compat\"\n" .
- "else\n" .
- " echo \"Unsupported Linux distribution after package check.\"\n" .
- " exit 1\n" .
- "fi\n\n" .
- "echo \"Cleaning up old TrustLab certificates...\"\n" .
- "rm -f \"\$TARGET_DIR/trustlab-*.crt\"\n\n";
-
- foreach ($certificates as $cert) {
- $cdnUrl = $cert->cert_path ? Storage::disk('r2-public')->url($cert->cert_path) : null;
- if (!$cdnUrl) continue;
-
- $filename = "trustlab-" . Str::slug($cert->common_name) . ".crt";
- $shContent .= "echo \"Downloading and deploying {$cert->common_name}...\"\n" .
- "curl -L --progress-bar \"{$cdnUrl}\" -o \"\$TARGET_DIR/{$filename}\"\n";
- }
-
- $shContent .= "\necho \"Finalizing installation with: \$UPDATE_CMD\"\n" .
- "\$UPDATE_CMD\n" .
- "echo \"All certificates installed successfully.\"\n" .
- "echo \"To verify, you can check: ls \$TARGET_DIR/trustlab-*\"\n";
-
- Storage::disk('r2-public')->delete('ca/bundles/trustlab-all.sh');
- Storage::disk('r2-public')->put('ca/bundles/trustlab-all.sh', $shContent, [
- 'visibility' => 'public',
- 'ContentType' => 'text/plain',
- 'CacheControl' => $cacheControl
- ]);
-
- // 2. Windows Bundle (.bat)
- $batContent = "@echo off\n" .
- "rem Generated at: {$now}\n" .
- "echo TrustLab - Installing all CA Certificates...\n";
-
- foreach ($certificates as $cert) {
- $cdnUrl = $cert->cert_path ? Storage::disk('r2-public')->url($cert->cert_path) : null;
- if (!$cdnUrl) continue;
-
- $store = $cert->ca_type === 'root' ? 'Root' : 'CA';
- $batContent .= "echo Installing {$cert->common_name} to {$store} store...\n" .
- "curl -L --progress-bar \"{$cdnUrl}\" -o \"%TEMP%\\trustlab-{$cert->uuid}.crt\"\n" .
- "certutil -addstore -f \"{$store}\" \"%TEMP%\\trustlab-{$cert->uuid}.crt\"\n" .
- "del \"%TEMP%\\trustlab-{$cert->uuid}.crt\"\n";
- }
- $batContent .= "echo Installation Complete.\npause";
-
- Storage::disk('r2-public')->delete('ca/bundles/trustlab-all.bat');
- Storage::disk('r2-public')->put('ca/bundles/trustlab-all.bat', $batContent, [
- 'visibility' => 'public',
- 'ContentType' => 'text/plain',
- 'CacheControl' => $cacheControl
- ]);
-
- // 3. macOS Bundle (.mobileconfig)
- $uuid1 = Str::uuid()->toString();
- $payloadContent = "";
-
- foreach ($certificates as $cert) {
- $certBase64 = base64_encode($cert->cert_content);
- $uuidSub = Str::uuid()->toString();
- $payloadType = $cert->ca_type === 'root' ? 'com.apple.security.root' : 'com.apple.security.pkcs1';
-
- $payloadContent .= " \n" .
- " PayloadCertificateFileName\n" .
- " {$cert->common_name}.crt\n" .
- " PayloadContent\n" .
- " {$certBase64}\n" .
- " PayloadDescription\n" .
- " TrustLab CA Certificate\n" .
- " PayloadDisplayName\n" .
- " {$cert->common_name}\n" .
- " PayloadIdentifier\n" .
- " com.trustlab.bundle.{$cert->uuid}\n" .
- " PayloadType\n" .
- " {$payloadType}\n" .
- " PayloadUUID\n" .
- " {$uuidSub}\n" .
- " PayloadVersion\n" .
- " 1\n" .
- " \n";
- }
-
- $macContent = "\n" .
- "\n" .
- "\n" .
- "\n" .
- " PayloadContent\n" .
- " \n" . $payloadContent . " \n" .
- " PayloadDescription\n" .
- " TrustLab All-in-One CA Bundle\n" .
- " PayloadDisplayName\n" .
- " TrustLab CA Bundle\n" .
- " PayloadIdentifier\n" .
- " com.trustlab.ca.bundle\n" .
- " PayloadRemovalDisallowed\n" .
- " \n" .
- " PayloadType\n" .
- " Configuration\n" .
- " PayloadUUID\n" .
- " {$uuid1}\n" .
- " PayloadVersion\n" .
- " 1\n" .
- "\n" .
- "";
-
- Storage::disk('r2-public')->delete('ca/bundles/trustlab-all.mobileconfig');
- Storage::disk('r2-public')->put('ca/bundles/trustlab-all.mobileconfig', $macContent, [
- 'visibility' => 'public',
- 'ContentType' => 'application/x-apple-aspen-config',
- 'CacheControl' => $cacheControl
- ]);
-
- return true;
- }
-
/**
* Legacy/Full Upload (Uploads everything)
*/
@@ -934,14 +586,19 @@ class OpenSslService
{
try {
$this->uploadPublicCertsOnly($cert);
- $this->uploadIndividualInstallersOnly($cert);
- $this->syncAllBundles();
+
+ // Delegate installer logic
+ $installerService = app(\App\Services\CaInstallerService::class);
+ $installerService->uploadIndividualInstallersOnly($cert);
+ $installerService->syncAllBundles();
+
return true;
} catch (\Exception $e) {
\Log::error("Failed to upload CA to R2: " . $e->getMessage());
return false;
}
}
+
/**
* Purge everything under the 'ca/' directory on the CDN.
*/