feat(ssl): Add full MariaDB SSL support
This commit is contained in:
		@@ -2,6 +2,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Actions\Database;
 | 
					namespace App\Actions\Database;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Helpers\SslHelper;
 | 
				
			||||||
 | 
					use App\Models\SslCertificate;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use Lorisleiva\Actions\Concerns\AsAction;
 | 
					use Lorisleiva\Actions\Concerns\AsAction;
 | 
				
			||||||
use Symfony\Component\Yaml\Yaml;
 | 
					use Symfony\Component\Yaml\Yaml;
 | 
				
			||||||
@@ -16,6 +18,8 @@ class StartMariadb
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public string $configuration_dir;
 | 
					    public string $configuration_dir;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private ?SslCertificate $ssl_certificate = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function handle(StandaloneMariadb $database)
 | 
					    public function handle(StandaloneMariadb $database)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->database = $database;
 | 
					        $this->database = $database;
 | 
				
			||||||
@@ -25,9 +29,57 @@ class StartMariadb
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $this->commands = [
 | 
					        $this->commands = [
 | 
				
			||||||
            "echo 'Starting database.'",
 | 
					            "echo 'Starting database.'",
 | 
				
			||||||
 | 
					            "echo 'Creating directories.'",
 | 
				
			||||||
            "mkdir -p $this->configuration_dir",
 | 
					            "mkdir -p $this->configuration_dir",
 | 
				
			||||||
 | 
					            "echo 'Directories created successfully.'",
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (! $this->database->enable_ssl) {
 | 
				
			||||||
 | 
					            $this->commands[] = "rm -rf $this->configuration_dir/ssl";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SslCertificate::where('resource_type', $this->database->getMorphClass())
 | 
				
			||||||
 | 
					                ->where('resource_id', $this->database->id)
 | 
				
			||||||
 | 
					                ->delete();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->database->fileStorages()
 | 
				
			||||||
 | 
					                ->where('resource_type', $this->database->getMorphClass())
 | 
				
			||||||
 | 
					                ->where('resource_id', $this->database->id)
 | 
				
			||||||
 | 
					                ->get()
 | 
				
			||||||
 | 
					                ->filter(function ($storage) {
 | 
				
			||||||
 | 
					                    return in_array($storage->mount_path, [
 | 
				
			||||||
 | 
					                        '/etc/mysql/certs/server.crt',
 | 
				
			||||||
 | 
					                        '/etc/mysql/certs/server.key',
 | 
				
			||||||
 | 
					                    ]);
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                ->each(function ($storage) {
 | 
				
			||||||
 | 
					                    $storage->delete();
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            $this->commands[] = "echo 'Setting up SSL for this database.'";
 | 
				
			||||||
 | 
					            $this->commands[] = "rm -rf $this->configuration_dir/ssl";
 | 
				
			||||||
 | 
					            $this->commands[] = "mkdir -p $this->configuration_dir/ssl";
 | 
				
			||||||
 | 
					            $server = $this->database->destination->server;
 | 
				
			||||||
 | 
					            $caCert = SslCertificate::where('server_id', $server->id)->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->ssl_certificate = SslCertificate::where('resource_type', $this->database->getMorphClass())
 | 
				
			||||||
 | 
					                ->where('resource_id', $this->database->id)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (! $this->ssl_certificate) {
 | 
				
			||||||
 | 
					                $this->commands[] = "echo 'No SSL certificate found, generating new SSL certificate for this database.'";
 | 
				
			||||||
 | 
					                $this->ssl_certificate = SslHelper::generateSslCertificate(
 | 
				
			||||||
 | 
					                    commonName: $this->database->uuid,
 | 
				
			||||||
 | 
					                    resourceType: $this->database->getMorphClass(),
 | 
				
			||||||
 | 
					                    resourceId: $this->database->id,
 | 
				
			||||||
 | 
					                    serverId: $server->id,
 | 
				
			||||||
 | 
					                    caCert: $caCert->ssl_certificate,
 | 
				
			||||||
 | 
					                    caKey: $caCert->ssl_private_key,
 | 
				
			||||||
 | 
					                    configurationDir: $this->configuration_dir,
 | 
				
			||||||
 | 
					                    mountPath: '/etc/mysql/certs',
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $persistent_storages = $this->generate_local_persistent_volumes();
 | 
					        $persistent_storages = $this->generate_local_persistent_volumes();
 | 
				
			||||||
        $persistent_file_volumes = $this->database->fileStorages()->get();
 | 
					        $persistent_file_volumes = $this->database->fileStorages()->get();
 | 
				
			||||||
        $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
 | 
					        $volume_names = $this->generate_local_persistent_volumes_only_volume_names();
 | 
				
			||||||
@@ -67,38 +119,64 @@ class StartMariadb
 | 
				
			|||||||
                ],
 | 
					                ],
 | 
				
			||||||
            ],
 | 
					            ],
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (! is_null($this->database->limits_cpuset)) {
 | 
					        if (! is_null($this->database->limits_cpuset)) {
 | 
				
			||||||
            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
					            data_set($docker_compose, "services.{$container_name}.cpuset", $this->database->limits_cpuset);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
 | 
					        if ($this->database->destination->server->isLogDrainEnabled() && $this->database->isLogDrainEnabled()) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['logging'] = generate_fluentd_configuration();
 | 
					            $docker_compose['services'][$container_name]['logging'] = generate_fluentd_configuration();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (count($this->database->ports_mappings_array) > 0) {
 | 
					        if (count($this->database->ports_mappings_array) > 0) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
 | 
					            $docker_compose['services'][$container_name]['ports'] = $this->database->ports_mappings_array;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (count($persistent_storages) > 0) {
 | 
					
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'] = $persistent_storages;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (count($persistent_file_volumes) > 0) {
 | 
					 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'] = $persistent_file_volumes->map(function ($item) {
 | 
					 | 
				
			||||||
                return "$item->fs_path:$item->mount_path";
 | 
					 | 
				
			||||||
            })->toArray();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (count($volume_names) > 0) {
 | 
					        if (count($volume_names) > 0) {
 | 
				
			||||||
            $docker_compose['volumes'] = $volume_names;
 | 
					            $docker_compose['volumes'] = $volume_names;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $docker_compose['services'][$container_name]['volumes'] ??= [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (count($persistent_storages) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = array_merge(
 | 
				
			||||||
 | 
					                $docker_compose['services'][$container_name]['volumes'],
 | 
				
			||||||
 | 
					                $persistent_storages
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (count($persistent_file_volumes) > 0) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['volumes'] = array_merge(
 | 
				
			||||||
 | 
					                $docker_compose['services'][$container_name]['volumes'],
 | 
				
			||||||
 | 
					                $persistent_file_volumes->map(function ($item) {
 | 
				
			||||||
 | 
					                    return "$item->fs_path:$item->mount_path";
 | 
				
			||||||
 | 
					                })->toArray()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
 | 
					        if (! is_null($this->database->mariadb_conf) || ! empty($this->database->mariadb_conf)) {
 | 
				
			||||||
            $docker_compose['services'][$container_name]['volumes'][] = [
 | 
					            $docker_compose['services'][$container_name]['volumes'] = array_merge(
 | 
				
			||||||
 | 
					                $docker_compose['services'][$container_name]['volumes'],
 | 
				
			||||||
 | 
					                [
 | 
				
			||||||
 | 
					                    [
 | 
				
			||||||
                        'type' => 'bind',
 | 
					                        'type' => 'bind',
 | 
				
			||||||
                        'source' => $this->configuration_dir.'/custom-config.cnf',
 | 
					                        'source' => $this->configuration_dir.'/custom-config.cnf',
 | 
				
			||||||
                        'target' => '/etc/mysql/conf.d/custom-config.cnf',
 | 
					                        'target' => '/etc/mysql/conf.d/custom-config.cnf',
 | 
				
			||||||
                        'read_only' => true,
 | 
					                        'read_only' => true,
 | 
				
			||||||
            ];
 | 
					                    ],
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Add custom docker run options
 | 
					        // Add custom docker run options
 | 
				
			||||||
        $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
 | 
					        $docker_run_options = convertDockerRunToCompose($this->database->custom_docker_run_options);
 | 
				
			||||||
        $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
 | 
					        $docker_compose = generateCustomDockerRunOptionsForDatabases($docker_run_options, $docker_compose, $container_name, $this->database->destination->network);
 | 
				
			||||||
 | 
					        if ($this->database->enable_ssl) {
 | 
				
			||||||
 | 
					            $docker_compose['services'][$container_name]['command'] = [
 | 
				
			||||||
 | 
					                'mysqld',
 | 
				
			||||||
 | 
					                '--ssl-cert=/etc/mysql/certs/server.crt',
 | 
				
			||||||
 | 
					                '--ssl-key=/etc/mysql/certs/server.key',
 | 
				
			||||||
 | 
					                '--require-secure-transport=1',
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
					        $docker_compose = Yaml::dump($docker_compose, 10);
 | 
				
			||||||
        $docker_compose_base64 = base64_encode($docker_compose);
 | 
					        $docker_compose_base64 = base64_encode($docker_compose);
 | 
				
			||||||
@@ -109,6 +187,9 @@ class StartMariadb
 | 
				
			|||||||
        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml pull";
 | 
				
			||||||
        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
 | 
					        $this->commands[] = "docker compose -f $this->configuration_dir/docker-compose.yml up -d";
 | 
				
			||||||
        $this->commands[] = "echo 'Database started.'";
 | 
					        $this->commands[] = "echo 'Database started.'";
 | 
				
			||||||
 | 
					        if ($this->database->enable_ssl) {
 | 
				
			||||||
 | 
					            $this->commands[] = executeInDocker($this->database->uuid, 'chown mariadb:mariadb /etc/mysql/certs/server.crt /etc/mysql/certs/server.key');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
 | 
					        return remote_process($this->commands, $database->destination->server, callEventOnFinish: 'DatabaseStatusChanged');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,9 @@ namespace App\Livewire\Project\Database\Mariadb;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use App\Actions\Database\StartDatabaseProxy;
 | 
					use App\Actions\Database\StartDatabaseProxy;
 | 
				
			||||||
use App\Actions\Database\StopDatabaseProxy;
 | 
					use App\Actions\Database\StopDatabaseProxy;
 | 
				
			||||||
 | 
					use App\Helpers\SslHelper;
 | 
				
			||||||
use App\Models\Server;
 | 
					use App\Models\Server;
 | 
				
			||||||
 | 
					use App\Models\SslCertificate;
 | 
				
			||||||
use App\Models\StandaloneMariadb;
 | 
					use App\Models\StandaloneMariadb;
 | 
				
			||||||
use Exception;
 | 
					use Exception;
 | 
				
			||||||
use Livewire\Component;
 | 
					use Livewire\Component;
 | 
				
			||||||
@@ -21,6 +23,8 @@ class General extends Component
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    public ?string $db_url_public = null;
 | 
					    public ?string $db_url_public = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public $certificateValidUntil = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $rules = [
 | 
					    protected $rules = [
 | 
				
			||||||
        'database.name' => 'required',
 | 
					        'database.name' => 'required',
 | 
				
			||||||
        'database.description' => 'nullable',
 | 
					        'database.description' => 'nullable',
 | 
				
			||||||
@@ -35,6 +39,8 @@ class General extends Component
 | 
				
			|||||||
        'database.public_port' => 'nullable|integer',
 | 
					        'database.public_port' => 'nullable|integer',
 | 
				
			||||||
        'database.is_log_drain_enabled' => 'nullable|boolean',
 | 
					        'database.is_log_drain_enabled' => 'nullable|boolean',
 | 
				
			||||||
        'database.custom_docker_run_options' => 'nullable',
 | 
					        'database.custom_docker_run_options' => 'nullable',
 | 
				
			||||||
 | 
					        'database.enable_ssl' => 'boolean',
 | 
				
			||||||
 | 
					        'database.ssl_mode' => 'nullable|string|in:PREFERRED,REQUIRED,VERIFY_CA,VERIFY_IDENTITY',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected $validationAttributes = [
 | 
					    protected $validationAttributes = [
 | 
				
			||||||
@@ -50,6 +56,8 @@ class General extends Component
 | 
				
			|||||||
        'database.is_public' => 'Is Public',
 | 
					        'database.is_public' => 'Is Public',
 | 
				
			||||||
        'database.public_port' => 'Public Port',
 | 
					        'database.public_port' => 'Public Port',
 | 
				
			||||||
        'database.custom_docker_run_options' => 'Custom Docker Options',
 | 
					        'database.custom_docker_run_options' => 'Custom Docker Options',
 | 
				
			||||||
 | 
					        'database.enable_ssl' => 'Enable SSL',
 | 
				
			||||||
 | 
					        'database.ssl_mode' => 'SSL Mode',
 | 
				
			||||||
    ];
 | 
					    ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function mount()
 | 
					    public function mount()
 | 
				
			||||||
@@ -57,6 +65,14 @@ class General extends Component
 | 
				
			|||||||
        $this->db_url = $this->database->internal_db_url;
 | 
					        $this->db_url = $this->database->internal_db_url;
 | 
				
			||||||
        $this->db_url_public = $this->database->external_db_url;
 | 
					        $this->db_url_public = $this->database->external_db_url;
 | 
				
			||||||
        $this->server = data_get($this->database, 'destination.server');
 | 
					        $this->server = data_get($this->database, 'destination.server');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $existingCert = SslCertificate::where('resource_type', $this->database->getMorphClass())
 | 
				
			||||||
 | 
					            ->where('resource_id', $this->database->id)
 | 
				
			||||||
 | 
					            ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($existingCert) {
 | 
				
			||||||
 | 
					            $this->certificateValidUntil = $existingCert->valid_until;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function instantSaveAdvanced()
 | 
					    public function instantSaveAdvanced()
 | 
				
			||||||
@@ -127,6 +143,52 @@ class General extends Component
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function instantSaveSSL()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $this->database->enable_ssl = $this->database->enable_ssl;
 | 
				
			||||||
 | 
					            $this->database->ssl_mode = $this->database->ssl_mode;
 | 
				
			||||||
 | 
					            $this->database->save();
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'SSL configuration updated.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function regenerateSslCertificate()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            $server = $this->database->destination->server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $existingCert = SslCertificate::where('resource_type', $this->database->getMorphClass())
 | 
				
			||||||
 | 
					                ->where('resource_id', $this->database->id)
 | 
				
			||||||
 | 
					                ->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (! $existingCert) {
 | 
				
			||||||
 | 
					                $this->dispatch('error', 'No existing SSL certificate found for this database.');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $caCert = SslCertificate::where('server_id', $server->id)->firstOrFail();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SslHelper::generateSslCertificate(
 | 
				
			||||||
 | 
					                commonName: $existingCert->common_name,
 | 
				
			||||||
 | 
					                subjectAlternativeNames: $existingCert->subject_alternative_names ?? [],
 | 
				
			||||||
 | 
					                resourceType: $existingCert->resource_type,
 | 
				
			||||||
 | 
					                resourceId: $existingCert->resource_id,
 | 
				
			||||||
 | 
					                serverId: $existingCert->server_id,
 | 
				
			||||||
 | 
					                caCert: $caCert->ssl_certificate,
 | 
				
			||||||
 | 
					                caKey: $caCert->ssl_private_key,
 | 
				
			||||||
 | 
					                configurationDir: $existingCert->configuration_dir,
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            $this->dispatch('success', 'SSL certificates have been regenerated. Please restart the database for changes to take effect.');
 | 
				
			||||||
 | 
					        } catch (Exception $e) {
 | 
				
			||||||
 | 
					            return handleError($e, $this);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function refresh(): void
 | 
					    public function refresh(): void
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        $this->database->refresh();
 | 
					        $this->database->refresh();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -218,7 +218,17 @@ class StandaloneMariadb extends BaseModel
 | 
				
			|||||||
    protected function internalDbUrl(): Attribute
 | 
					    protected function internalDbUrl(): Attribute
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        return new Attribute(
 | 
					        return new Attribute(
 | 
				
			||||||
            get: fn () => "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}",
 | 
					            get: function () {
 | 
				
			||||||
 | 
					                $url = "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->uuid}:3306/{$this->mariadb_database}";
 | 
				
			||||||
 | 
					                if ($this->enable_ssl) {
 | 
				
			||||||
 | 
					                    $url .= "?ssl-mode={$this->ssl_mode}";
 | 
				
			||||||
 | 
					                    if (in_array($this->ssl_mode, ['VERIFY_CA', 'VERIFY_IDENTITY'])) {
 | 
				
			||||||
 | 
					                        $url .= '&ssl-ca=/etc/ssl/certs/coolify-ca.crt';
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                return $url;
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -227,7 +237,15 @@ class StandaloneMariadb extends BaseModel
 | 
				
			|||||||
        return new Attribute(
 | 
					        return new Attribute(
 | 
				
			||||||
            get: function () {
 | 
					            get: function () {
 | 
				
			||||||
                if ($this->is_public && $this->public_port) {
 | 
					                if ($this->is_public && $this->public_port) {
 | 
				
			||||||
                    return "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
 | 
					                    $url = "mysql://{$this->mariadb_user}:{$this->mariadb_password}@{$this->destination->server->getIp}:{$this->public_port}/{$this->mariadb_database}";
 | 
				
			||||||
 | 
					                    if ($this->enable_ssl) {
 | 
				
			||||||
 | 
					                        $url .= "?ssl-mode={$this->ssl_mode}";
 | 
				
			||||||
 | 
					                        if (in_array($this->ssl_mode, ['VERIFY_CA', 'VERIFY_IDENTITY'])) {
 | 
				
			||||||
 | 
					                            $url .= '&ssl-ca=/etc/ssl/certs/coolify-ca.crt';
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    return $url;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -65,6 +65,50 @@
 | 
				
			|||||||
                    type="password" readonly wire:model="db_url_public" />
 | 
					                    type="password" readonly wire:model="db_url_public" />
 | 
				
			||||||
            @endif
 | 
					            @endif
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <div class="flex flex-col gap-2">
 | 
				
			||||||
 | 
					            <div class="flex items-center justify-between py-2">
 | 
				
			||||||
 | 
					                <div class="flex items-center justify-between w-full">
 | 
				
			||||||
 | 
					                    <h3>SSL Configuration</h3>
 | 
				
			||||||
 | 
					                    @if($database->enable_ssl && $certificateValidUntil)
 | 
				
			||||||
 | 
					                        <x-modal-confirmation
 | 
				
			||||||
 | 
					                            title="Regenerate SSL Certificates"
 | 
				
			||||||
 | 
					                            buttonTitle="Regenerate SSL Certificates"
 | 
				
			||||||
 | 
					                            :actions="['The SSL certificate of this database will be regenerated.','You must restart the database after regenerating the certificate to start using the new certificate.']"
 | 
				
			||||||
 | 
					                            submitAction="regenerateSslCertificate"
 | 
				
			||||||
 | 
					                            :confirmWithText="false"
 | 
				
			||||||
 | 
					                            :confirmWithPassword="false"
 | 
				
			||||||
 | 
					                        />
 | 
				
			||||||
 | 
					                    @endif
 | 
				
			||||||
 | 
					                </div>
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					            @if($database->enable_ssl && $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>
 | 
				
			||||||
 | 
					        <div class="flex flex-col gap-2">
 | 
				
			||||||
 | 
					            <div class="flex flex-col gap-2">
 | 
				
			||||||
 | 
					                <x-forms.checkbox id="database.enable_ssl" label="Enable SSL" wire:model.live="database.enable_ssl" instantSave="instantSaveSSL" />
 | 
				
			||||||
 | 
					                @if($database->enable_ssl)
 | 
				
			||||||
 | 
					                    <x-forms.select id="database.ssl_mode" label="SSL Mode" wire:model.live="database.ssl_mode" instantSave="instantSaveSSL"
 | 
				
			||||||
 | 
					                        helper="Choose the SSL verification mode for MariaDB connections">
 | 
				
			||||||
 | 
					                        <option value="PREFERRED">PREFERRED</option>
 | 
				
			||||||
 | 
					                        <option value="REQUIRED">REQUIRED</option>
 | 
				
			||||||
 | 
					                        <option value="VERIFY_CA">VERIFY_CA</option>
 | 
				
			||||||
 | 
					                        <option value="VERIFY_IDENTITY">VERIFY_IDENTITY</option>
 | 
				
			||||||
 | 
					                    </x-forms.select>
 | 
				
			||||||
 | 
					                @endif
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
            <div class="flex flex-col py-2 w-64">
 | 
					            <div class="flex flex-col py-2 w-64">
 | 
				
			||||||
                <div class="flex items-center gap-2 pb-2">
 | 
					                <div class="flex items-center gap-2 pb-2">
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user