feat(ca-certificate): add CA certificate management functionality with UI integration and routing
This commit is contained in:
		@@ -43,7 +43,7 @@ class Terminal extends Component
 | 
				
			|||||||
    #[On('send-terminal-command')]
 | 
					    #[On('send-terminal-command')]
 | 
				
			||||||
    public function sendTerminalCommand($isContainer, $identifier, $serverUuid)
 | 
					    public function sendTerminalCommand($isContainer, $identifier, $serverUuid)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->firstOrFail();
 | 
					        $server = Server::ownedByCurrentTeam()->whereUuid($serverUuid)->where('settings.is_terminal_enabled', true)->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($isContainer) {
 | 
					        if ($isContainer) {
 | 
				
			||||||
            // Validate container identifier format (alphanumeric, dashes, and underscores only)
 | 
					            // Validate container identifier format (alphanumeric, dashes, and underscores only)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,12 +2,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Livewire\Server;
 | 
					namespace App\Livewire\Server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use App\Helpers\SslHelper;
 | 
					 | 
				
			||||||
use App\Jobs\RegenerateSslCertJob;
 | 
					 | 
				
			||||||
use App\Models\InstanceSettings;
 | 
					use App\Models\InstanceSettings;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
use App\Models\SslCertificate;
 | 
					 | 
				
			||||||
use Carbon\Carbon;
 | 
					 | 
				
			||||||
use Illuminate\Support\Facades\Auth;
 | 
					use Illuminate\Support\Facades\Auth;
 | 
				
			||||||
use Illuminate\Support\Facades\Hash;
 | 
					use Illuminate\Support\Facades\Hash;
 | 
				
			||||||
use Livewire\Attributes\Validate;
 | 
					use Livewire\Attributes\Validate;
 | 
				
			||||||
@@ -17,14 +13,6 @@ class Advanced extends Component
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    public Server $server;
 | 
					    public Server $server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public ?SslCertificate $caCertificate = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public $showCertificate = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public $certificateContent = '';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public ?Carbon $certificateValidUntil = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public array $parameters = [];
 | 
					    public array $parameters = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #[Validate(['string'])]
 | 
					    #[Validate(['string'])]
 | 
				
			||||||
@@ -48,27 +36,12 @@ class Advanced extends Component
 | 
				
			|||||||
            $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
 | 
					            $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
 | 
				
			||||||
            $this->parameters = get_route_parameters();
 | 
					            $this->parameters = get_route_parameters();
 | 
				
			||||||
            $this->syncData();
 | 
					            $this->syncData();
 | 
				
			||||||
            $this->loadCaCertificate();
 | 
					
 | 
				
			||||||
        } catch (\Throwable) {
 | 
					        } catch (\Throwable) {
 | 
				
			||||||
            return redirect()->route('server.index');
 | 
					            return redirect()->route('server.index');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function loadCaCertificate()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $this->caCertificate = SslCertificate::where('server_id', $this->server->id)->where('is_ca_certificate', true)->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 toggleTerminal($password)
 | 
					    public function toggleTerminal($password)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
@@ -100,78 +73,6 @@ class Advanced extends Component
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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->loadCaCertificate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                $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 {
 | 
					 | 
				
			||||||
            SslHelper::generateSslCertificate(
 | 
					 | 
				
			||||||
                commonName: 'Coolify CA Certificate',
 | 
					 | 
				
			||||||
                serverId: $this->server->id,
 | 
					 | 
				
			||||||
                isCaCertificate: true,
 | 
					 | 
				
			||||||
                validityDays: 10 * 365
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $this->loadCaCertificate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            $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()
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        $caCertPath = config('constants.coolify.base_config_path').'/ssl/';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        $commands = collect([
 | 
					 | 
				
			||||||
            "mkdir -p $caCertPath",
 | 
					 | 
				
			||||||
            "chown -R 9999:root $caCertPath",
 | 
					 | 
				
			||||||
            "chmod -R 700 $caCertPath",
 | 
					 | 
				
			||||||
            "rm -rf $caCertPath/coolify-ca.crt",
 | 
					 | 
				
			||||||
            "echo '{$this->certificateContent}' > $caCertPath/coolify-ca.crt",
 | 
					 | 
				
			||||||
            "chmod 644 $caCertPath/coolify-ca.crt",
 | 
					 | 
				
			||||||
        ]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        remote_process($commands, $this->server);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public function syncData(bool $toModel = false)
 | 
					    public function syncData(bool $toModel = false)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        if ($toModel) {
 | 
					        if ($toModel) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										128
									
								
								app/Livewire/Server/CaCertificate/Show.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								app/Livewire/Server/CaCertificate/Show.php
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,128 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Livewire\Server\CaCertificate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Helpers\SslHelper;
 | 
				
			||||||
 | 
					use App\Jobs\RegenerateSslCertJob;
 | 
				
			||||||
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\SslCertificate;
 | 
				
			||||||
 | 
					use Illuminate\Support\Carbon;
 | 
				
			||||||
 | 
					use Livewire\Attributes\Locked;
 | 
				
			||||||
 | 
					use Livewire\Component;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Show extends Component
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    #[Locked]
 | 
				
			||||||
 | 
					    public Server $server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ?SslCertificate $caCertificate = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $showCertificate = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $certificateContent = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public ?Carbon $certificateValidUntil = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function mount(string $server_uuid)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->server = Server::ownedByCurrentTeam()->whereUuid($server_uuid)->firstOrFail();
 | 
				
			||||||
 | 
					            $this->loadCaCertificate();
 | 
				
			||||||
 | 
					        } catch (\Throwable $e) {
 | 
				
			||||||
 | 
					            return redirect()->route('server.index');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function loadCaCertificate()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->caCertificate = SslCertificate::where('server_id', $this->server->id)->where('is_ca_certificate', true)->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->loadCaCertificate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                $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 {
 | 
				
			||||||
 | 
					            SslHelper::generateSslCertificate(
 | 
				
			||||||
 | 
					                commonName: 'Coolify CA Certificate',
 | 
				
			||||||
 | 
					                serverId: $this->server->id,
 | 
				
			||||||
 | 
					                isCaCertificate: true,
 | 
				
			||||||
 | 
					                validityDays: 10 * 365
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->loadCaCertificate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $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()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $caCertPath = config('constants.coolify.base_config_path').'/ssl/';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $commands = collect([
 | 
				
			||||||
 | 
					            "mkdir -p $caCertPath",
 | 
				
			||||||
 | 
					            "chown -R 9999:root $caCertPath",
 | 
				
			||||||
 | 
					            "chmod -R 700 $caCertPath",
 | 
				
			||||||
 | 
					            "rm -rf $caCertPath/coolify-ca.crt",
 | 
				
			||||||
 | 
					            "echo '{$this->certificateContent}' > $caCertPath/coolify-ca.crt",
 | 
				
			||||||
 | 
					            "chmod 644 $caCertPath/coolify-ca.crt",
 | 
				
			||||||
 | 
					        ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        remote_process($commands, $this->server);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function render()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return view('livewire.server.ca-certificate.show');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -9,6 +9,9 @@
 | 
				
			|||||||
    <a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
 | 
					    <a class="menu-item {{ $activeMenu === 'private-key' ? 'menu-item-active' : '' }}"
 | 
				
			||||||
        href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
 | 
					        href="{{ route('server.private-key', ['server_uuid' => $server->uuid]) }}">Private Key
 | 
				
			||||||
    </a>
 | 
					    </a>
 | 
				
			||||||
 | 
					    <a class="menu-item {{ $activeMenu === 'ca-certificate' ? 'menu-item-active' : '' }}"
 | 
				
			||||||
 | 
					        href="{{ route('server.ca-certificate', ['server_uuid' => $server->uuid]) }}">CA Certificate
 | 
				
			||||||
 | 
					    </a>
 | 
				
			||||||
    @if (!$server->isLocalhost())
 | 
					    @if (!$server->isLocalhost())
 | 
				
			||||||
        <a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
 | 
					        <a class="menu-item {{ $activeMenu === 'cloudflare-tunnels' ? 'menu-item-active' : '' }}"
 | 
				
			||||||
            href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}">Cloudflare
 | 
					            href="{{ route('server.cloudflare-tunnels', ['server_uuid' => $server->uuid]) }}">Cloudflare
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,7 +69,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                <div class="flex flex-col">
 | 
					                <div class="flex flex-col">
 | 
				
			||||||
                    <h3>Builds</h3>
 | 
					                    <h3>Builds</h3>
 | 
				
			||||||
                    <div>Customize the build process.</div>
 | 
					 | 
				
			||||||
                    <div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
 | 
					                    <div class="flex flex-wrap gap-2 sm:flex-nowrap pt-4">
 | 
				
			||||||
                        <x-forms.input id="concurrentBuilds" label="Number of concurrent builds" required
 | 
					                        <x-forms.input id="concurrentBuilds" label="Number of concurrent builds" required
 | 
				
			||||||
                            helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
 | 
					                            helper="You can specify the number of simultaneous build processes/deployments that should run concurrently." />
 | 
				
			||||||
@@ -78,85 +77,6 @@
 | 
				
			|||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <div class="flex flex-col gap-4 pt-8">
 | 
					 | 
				
			||||||
                <h3>CA SSL Certificate</h3>
 | 
					 | 
				
			||||||
                <div class="flex gap-2">
 | 
					 | 
				
			||||||
                    <x-modal-confirmation title="Confirm changing of CA Certificate?" buttonTitle="Save Certificate"
 | 
					 | 
				
			||||||
                        submitAction="saveCaCertificate" :actions="[
 | 
					 | 
				
			||||||
                            'This will overwrite the existing CA certificate at /data/coolify/ssl/coolify-ca.crt with your custom CA certificate.',
 | 
					 | 
				
			||||||
                            'This will regenerate all SSL certificates for databases on this server and it will sign them with your custom CA.',
 | 
					 | 
				
			||||||
                            'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with your new CA certificate.',
 | 
					 | 
				
			||||||
                            'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
 | 
					 | 
				
			||||||
                        ]"
 | 
					 | 
				
			||||||
                        confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
 | 
					 | 
				
			||||||
                        step3ButtonText="Save Certificate">
 | 
					 | 
				
			||||||
                    </x-modal-confirmation>
 | 
					 | 
				
			||||||
                    <x-modal-confirmation title="Confirm Regenerate Certificate?" buttonTitle="Regenerate Certificate"
 | 
					 | 
				
			||||||
                        submitAction="regenerateCaCertificate" :actions="[
 | 
					 | 
				
			||||||
                            'This will generate a new CA certificate at /data/coolify/ssl/coolify-ca.crt and replace the existing one.',
 | 
					 | 
				
			||||||
                            'This will regenerate all SSL certificates for databases on this server and it will sign them with the new CA certificate.',
 | 
					 | 
				
			||||||
                            'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with the new CA certificate.',
 | 
					 | 
				
			||||||
                            'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
 | 
					 | 
				
			||||||
                        ]"
 | 
					 | 
				
			||||||
                        confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
 | 
					 | 
				
			||||||
                        step3ButtonText="Regenerate Certificate">
 | 
					 | 
				
			||||||
                    </x-modal-confirmation>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div class="space-y-4">
 | 
					 | 
				
			||||||
                    <div class="text-sm">
 | 
					 | 
				
			||||||
                        <p class="font-medium mb-2">Recommended Configuration:</p>
 | 
					 | 
				
			||||||
                        <ul class="list-disc pl-5 space-y-1">
 | 
					 | 
				
			||||||
                            <li>Mount this CA certificate of Coolify into all containers that need to connect to one of
 | 
					 | 
				
			||||||
                                your databases over SSL. You can see and copy the bind mount below.</li>
 | 
					 | 
				
			||||||
                            <li>Read more when and why this is needed <a class="underline"
 | 
					 | 
				
			||||||
                                    href="https://coolify.io/docs/databases/ssl" target="_blank">here</a>.</li>
 | 
					 | 
				
			||||||
                        </ul>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    <div class="relative">
 | 
					 | 
				
			||||||
                        <x-forms.copy-button
 | 
					 | 
				
			||||||
                            text="- /data/coolify/ssl/coolify-ca.crt:/etc/ssl/certs/coolify-ca.crt:ro" />
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
                <div>
 | 
					 | 
				
			||||||
                    <div class="flex items-center justify-between mb-2">
 | 
					 | 
				
			||||||
                        <div class="flex items-center gap-2">
 | 
					 | 
				
			||||||
                            <span class="text-sm">CA Certificate</span>
 | 
					 | 
				
			||||||
                            @if ($certificateValidUntil)
 | 
					 | 
				
			||||||
                                <span class="text-sm">(Valid until:
 | 
					 | 
				
			||||||
                                    @if (now()->gt($certificateValidUntil))
 | 
					 | 
				
			||||||
                                        <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} -
 | 
					 | 
				
			||||||
                                            Expired)</span>
 | 
					 | 
				
			||||||
                                    @elseif(now()->addDays(30)->gt($certificateValidUntil))
 | 
					 | 
				
			||||||
                                        <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} -
 | 
					 | 
				
			||||||
                                            Expiring soon)</span>
 | 
					 | 
				
			||||||
                                    @else
 | 
					 | 
				
			||||||
                                        <span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }})</span>
 | 
					 | 
				
			||||||
                                    @endif
 | 
					 | 
				
			||||||
                                </span>
 | 
					 | 
				
			||||||
                            @endif
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                        <x-forms.button wire:click="toggleCertificate" type="button" class="py-1! px-2! text-sm">
 | 
					 | 
				
			||||||
                            {{ $showCertificate ? 'Hide' : 'Show' }}
 | 
					 | 
				
			||||||
                        </x-forms.button>
 | 
					 | 
				
			||||||
                    </div>
 | 
					 | 
				
			||||||
                    @if ($showCertificate)
 | 
					 | 
				
			||||||
                        <textarea class="w-full h-[370px] input" wire:model="certificateContent"
 | 
					 | 
				
			||||||
                            placeholder="Paste or edit CA certificate content here..."></textarea>
 | 
					 | 
				
			||||||
                    @else
 | 
					 | 
				
			||||||
                        <div class="w-full h-[370px] input">
 | 
					 | 
				
			||||||
                            <div class="h-full flex flex-col items-center justify-center text-gray-300">
 | 
					 | 
				
			||||||
                                <div class="mb-2">
 | 
					 | 
				
			||||||
                                    ━━━━━━━━ CERTIFICATE CONTENT ━━━━━━━━
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                                <div class="text-sm">
 | 
					 | 
				
			||||||
                                    Click "Show" to view or edit
 | 
					 | 
				
			||||||
                                </div>
 | 
					 | 
				
			||||||
                            </div>
 | 
					 | 
				
			||||||
                        </div>
 | 
					 | 
				
			||||||
                    @endif
 | 
					 | 
				
			||||||
                </div>
 | 
					 | 
				
			||||||
            </div>
 | 
					 | 
				
			||||||
        </form>
 | 
					        </form>
 | 
				
			||||||
    </div>
 | 
					    </div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					<div>
 | 
				
			||||||
 | 
					    <x-slot:title>
 | 
				
			||||||
 | 
					        {{ data_get_str($server, 'name')->limit(10) }} > CA Certificate | Coolify
 | 
				
			||||||
 | 
					    </x-slot>
 | 
				
			||||||
 | 
					    <x-server.navbar :server="$server" />
 | 
				
			||||||
 | 
					    <div class="flex flex-col h-full gap-8 sm:flex-row">
 | 
				
			||||||
 | 
					        <x-server.sidebar :server="$server" activeMenu="ca-certificate" />
 | 
				
			||||||
 | 
					        <div class="flex flex-col gap-4">
 | 
				
			||||||
 | 
					            <div class="flex items-center gap-2">
 | 
				
			||||||
 | 
					                <h3>CA SSL Certificate</h3>
 | 
				
			||||||
 | 
					                <div class="flex gap-2">
 | 
				
			||||||
 | 
					                    <x-modal-confirmation title="Confirm changing of CA Certificate?" buttonTitle="Save"
 | 
				
			||||||
 | 
					                        submitAction="saveCaCertificate" :actions="[
 | 
				
			||||||
 | 
					                            'This will overwrite the existing CA certificate at /data/coolify/ssl/coolify-ca.crt with your custom CA certificate.',
 | 
				
			||||||
 | 
					                            'This will regenerate all SSL certificates for databases on this server and it will sign them with your custom CA.',
 | 
				
			||||||
 | 
					                            'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with your new CA certificate.',
 | 
				
			||||||
 | 
					                            'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
 | 
				
			||||||
 | 
					                        ]"
 | 
				
			||||||
 | 
					                        confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
 | 
				
			||||||
 | 
					                        step3ButtonText="Save Certificate">
 | 
				
			||||||
 | 
					                    </x-modal-confirmation>
 | 
				
			||||||
 | 
					                    <x-modal-confirmation title="Confirm Regenerate Certificate?" buttonTitle="Regenerate "
 | 
				
			||||||
 | 
					                        submitAction="regenerateCaCertificate" :actions="[
 | 
				
			||||||
 | 
					                            'This will generate a new CA certificate at /data/coolify/ssl/coolify-ca.crt and replace the existing one.',
 | 
				
			||||||
 | 
					                            'This will regenerate all SSL certificates for databases on this server and it will sign them with the new CA certificate.',
 | 
				
			||||||
 | 
					                            'You must manually redeploy all your databases on this server so that they use the new SSL certificates singned with the new CA certificate.',
 | 
				
			||||||
 | 
					                            'Because of caching, you probably also need to redeploy all your resources on this server that are using this CA certificate.',
 | 
				
			||||||
 | 
					                        ]"
 | 
				
			||||||
 | 
					                        confirmationText="/data/coolify/ssl/coolify-ca.crt" shortConfirmationLabel="CA Certificate Path"
 | 
				
			||||||
 | 
					                        step3ButtonText="Regenerate Certificate">
 | 
				
			||||||
 | 
					                    </x-modal-confirmation>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div class="space-y-4">
 | 
				
			||||||
 | 
					                <div class="text-sm">
 | 
				
			||||||
 | 
					                    <p class="font-medium mb-2">Recommended Configuration:</p>
 | 
				
			||||||
 | 
					                    <ul class="list-disc pl-5 space-y-1">
 | 
				
			||||||
 | 
					                        <li>Mount this CA certificate of Coolify into all containers that need to connect to one of
 | 
				
			||||||
 | 
					                            your databases over SSL. You can see and copy the bind mount below.</li>
 | 
				
			||||||
 | 
					                        <li>Read more when and why this is needed <a class="underline dark:text-white"
 | 
				
			||||||
 | 
					                                href="https://coolify.io/docs/databases/ssl" target="_blank">here</a>.</li>
 | 
				
			||||||
 | 
					                    </ul>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                <div class="relative">
 | 
				
			||||||
 | 
					                    <x-forms.copy-button text="- /data/coolify/ssl/coolify-ca.crt:/etc/ssl/certs/coolify-ca.crt:ro" />
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            <div>
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-between mb-2">
 | 
				
			||||||
 | 
					                    <div class="flex items-center gap-2">
 | 
				
			||||||
 | 
					                        <span class="text-sm">CA Certificate</span>
 | 
				
			||||||
 | 
					                        @if ($certificateValidUntil)
 | 
				
			||||||
 | 
					                            <span class="text-sm">(Valid until:
 | 
				
			||||||
 | 
					                                @if (now()->gt($certificateValidUntil))
 | 
				
			||||||
 | 
					                                    <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} -
 | 
				
			||||||
 | 
					                                        Expired)</span>
 | 
				
			||||||
 | 
					                                @elseif(now()->addDays(30)->gt($certificateValidUntil))
 | 
				
			||||||
 | 
					                                    <span class="text-red-500">{{ $certificateValidUntil->format('d.m.Y H:i:s') }} -
 | 
				
			||||||
 | 
					                                        Expiring soon)</span>
 | 
				
			||||||
 | 
					                                @else
 | 
				
			||||||
 | 
					                                    <span>{{ $certificateValidUntil->format('d.m.Y H:i:s') }})</span>
 | 
				
			||||||
 | 
					                                @endif
 | 
				
			||||||
 | 
					                            </span>
 | 
				
			||||||
 | 
					                        @endif
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                    <x-forms.button wire:click="toggleCertificate" type="button" class="py-1! px-2! text-sm">
 | 
				
			||||||
 | 
					                        {{ $showCertificate ? 'Hide' : 'Show' }}
 | 
				
			||||||
 | 
					                    </x-forms.button>
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					                @if ($showCertificate)
 | 
				
			||||||
 | 
					                    <textarea class="w-full h-[370px] input" wire:model="certificateContent"
 | 
				
			||||||
 | 
					                        placeholder="Paste or edit CA certificate content here..."></textarea>
 | 
				
			||||||
 | 
					                @else
 | 
				
			||||||
 | 
					                    <div class="w-full h-[370px] input">
 | 
				
			||||||
 | 
					                        <div class="h-full flex flex-col items-center justify-center text-gray-300">
 | 
				
			||||||
 | 
					                            <div class="mb-2">
 | 
				
			||||||
 | 
					                                ━━━━━━━━ CERTIFICATE CONTENT ━━━━━━━━
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                            <div class="text-sm">
 | 
				
			||||||
 | 
					                                Click "Show" to view or edit
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                @endif
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
@@ -37,6 +37,7 @@ use App\Livewire\Security\ApiTokens;
 | 
				
			|||||||
use App\Livewire\Security\PrivateKey\Index as SecurityPrivateKeyIndex;
 | 
					use App\Livewire\Security\PrivateKey\Index as SecurityPrivateKeyIndex;
 | 
				
			||||||
use App\Livewire\Security\PrivateKey\Show as SecurityPrivateKeyShow;
 | 
					use App\Livewire\Security\PrivateKey\Show as SecurityPrivateKeyShow;
 | 
				
			||||||
use App\Livewire\Server\Advanced as ServerAdvanced;
 | 
					use App\Livewire\Server\Advanced as ServerAdvanced;
 | 
				
			||||||
 | 
					use App\Livewire\Server\CaCertificate\Show as CaCertificateShow;
 | 
				
			||||||
use App\Livewire\Server\Charts as ServerCharts;
 | 
					use App\Livewire\Server\Charts as ServerCharts;
 | 
				
			||||||
use App\Livewire\Server\CloudflareTunnels;
 | 
					use App\Livewire\Server\CloudflareTunnels;
 | 
				
			||||||
use App\Livewire\Server\Delete as DeleteServer;
 | 
					use App\Livewire\Server\Delete as DeleteServer;
 | 
				
			||||||
@@ -242,6 +243,7 @@ Route::middleware(['auth', 'verified'])->group(function () {
 | 
				
			|||||||
        Route::get('/', ServerShow::class)->name('server.show');
 | 
					        Route::get('/', ServerShow::class)->name('server.show');
 | 
				
			||||||
        Route::get('/advanced', ServerAdvanced::class)->name('server.advanced');
 | 
					        Route::get('/advanced', ServerAdvanced::class)->name('server.advanced');
 | 
				
			||||||
        Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
 | 
					        Route::get('/private-key', PrivateKeyShow::class)->name('server.private-key');
 | 
				
			||||||
 | 
					        Route::get('/ca-certificate', CaCertificateShow::class)->name('server.ca-certificate');
 | 
				
			||||||
        Route::get('/resources', ResourcesShow::class)->name('server.resources');
 | 
					        Route::get('/resources', ResourcesShow::class)->name('server.resources');
 | 
				
			||||||
        Route::get('/cloudflare-tunnels', CloudflareTunnels::class)->name('server.cloudflare-tunnels');
 | 
					        Route::get('/cloudflare-tunnels', CloudflareTunnels::class)->name('server.cloudflare-tunnels');
 | 
				
			||||||
        Route::get('/destinations', ServerDestinations::class)->name('server.destinations');
 | 
					        Route::get('/destinations', ServerDestinations::class)->name('server.destinations');
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user