diff --git a/app/Jobs/RegenerateSslCertJob.php b/app/Jobs/RegenerateSslCertJob.php index 387408b89..a623f696c 100644 --- a/app/Jobs/RegenerateSslCertJob.php +++ b/app/Jobs/RegenerateSslCertJob.php @@ -51,7 +51,8 @@ class RegenerateSslCertJob implements ShouldQueue resourceType: $certificate->resource_type, resourceId: $certificate->resource_id, serverId: $certificate->server_id, - validityDays: 365 + validityDays: 365, + configurationDir: $certificate->configuration_dir, ); $regenerated->push($certificate); } catch (\Exception $e) { diff --git a/app/Livewire/Server/Advanced.php b/app/Livewire/Server/Advanced.php index b269c916f..195b54d18 100644 --- a/app/Livewire/Server/Advanced.php +++ b/app/Livewire/Server/Advanced.php @@ -2,7 +2,10 @@ namespace App\Livewire\Server; +use App\Helpers\SslHelper; +use App\Jobs\RegenerateSslCertJob; use App\Models\Server; +use App\Models\SslCertificate; use Livewire\Attributes\Validate; use Livewire\Component; @@ -10,6 +13,14 @@ class Advanced extends Component { public Server $server; + public ?SslCertificate $caCertificate = null; + + public $showCertificate = false; + + public $certificateContent = ''; + + public $certificateValidUntil = null; + public array $parameters = []; #[Validate(['string'])] @@ -30,11 +41,98 @@ class Advanced extends Component $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail(); $this->parameters = get_route_parameters(); $this->syncData(); + $this->loadCaCertificate(); } catch (\Throwable) { return redirect()->route('server.index'); } } + public function loadCaCertificate() + { + $this->caCertificate = SslCertificate::where('server_id', $this->server->id) + ->where('resource_type', null) + ->where('resource_id', null) + ->first(); + + if ($this->caCertificate) { + $this->certificateContent = $this->caCertificate->ssl_certificate; + $this->certificateValidUntil = $this->caCertificate->valid_until; + } + } + + public function toggleCertificate() + { + $this->showCertificate = ! $this->showCertificate; + } + + public function saveCaCertificate() + { + try { + if (! $this->certificateContent) { + throw new \Exception('Certificate content cannot be empty.'); + } + + if (! openssl_x509_read($this->certificateContent)) { + throw new \Exception('Invalid certificate format.'); + } + + if ($this->caCertificate) { + $this->caCertificate->ssl_certificate = $this->certificateContent; + $this->caCertificate->save(); + + $this->writeCertificateToServer(); + + dispatch(new RegenerateSslCertJob( + server_id: $this->server->id, + force_regeneration: true + )); + } + $this->dispatch('success', 'CA Certificate saved successfully.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + public function regenerateCaCertificate() + { + try { + $caCert = SslHelper::generateSslCertificate( + commonName: 'Coolify CA Certificate', + serverId: $this->server->id, + isCaCertificate: true, + validityDays: 15 * 365 + ); + + $this->writeCertificateToServer(); + + dispatch(new RegenerateSslCertJob( + server_id: $this->server->id, + force_regeneration: true + )); + + $this->loadCaCertificate(); + $this->dispatch('success', 'CA Certificate regenerated successfully.'); + } catch (\Throwable $e) { + return handleError($e, $this); + } + } + + private function writeCertificateToServer() + { + $serverCertPath = config('constants.coolify.base_config_path').'/ssl/'; + + $commands = collect([ + "mkdir -p $serverCertPath", + "chown -R 9999:root $serverCertPath", + "chmod -R 700 $serverCertPath", + "rm -f $serverCertPath/coolify-ca.crt", + "echo '{$this->caCertificate->ssl_certificate}' > $serverCertPath/coolify-ca.crt", + "chmod 644 $serverCertPath/coolify-ca.crt", + ]); + + remote_process($commands, $this->server); + } + public function syncData(bool $toModel = false) { if ($toModel) {