From 4e7d09035a07e8b2768dbb936c5200803d4cbbe1 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 18 Dec 2024 09:47:21 +0100 Subject: [PATCH 1/3] Support encrypted connections to MySQL/Maria and Postgres --- config/autoload/entity-manager.global.php | 30 +++++++++++++++-------- module/Core/src/Config/EnvVars.php | 2 ++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/config/autoload/entity-manager.global.php b/config/autoload/entity-manager.global.php index 4338a8b67..1bd3db444 100644 --- a/config/autoload/entity-manager.global.php +++ b/config/autoload/entity-manager.global.php @@ -12,9 +12,10 @@ return (static function (): array { $driver = EnvVars::DB_DRIVER->loadFromEnv(); + $useEncryption = (bool) EnvVars::DB_USE_ENCRYPTION->loadFromEnv(); $isMysqlCompatible = contains($driver, ['maria', 'mysql']); - $resolveDriver = static fn () => match ($driver) { + $doctrineDriver = match ($driver) { 'postgres' => 'pdo_pgsql', 'mssql' => 'pdo_sqlsrv', default => 'pdo_mysql', @@ -23,31 +24,40 @@ $value = $envVar->loadFromEnv(); return $value === null ? null : (string) $value; }; - $resolveCharset = static fn () => match ($driver) { + $charset = match ($driver) { // This does not determine charsets or collations in tables or columns, but the charset used in the data // flowing in the connection, so it has to match what has been set in the database. 'maria', 'mysql' => 'utf8mb4', 'postgres' => 'utf8', default => null, }; - - $resolveConnection = static fn () => match ($driver) { + $driverOptions = match ($driver) { + 'mssql' => ['TrustServerCertificate' => 'true'], + 'maria', 'mysql' => ! $useEncryption ? [] : [ + 1007 => true, // PDO::MYSQL_ATTR_SSL_KEY: Require using SSL + 1014 => false, // PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT: Trust any certificate + ], + 'postgres' => ! $useEncryption ? [] : [ + 'sslmode' => 'require', // Require connections to be encrypted + 'sslrootcert' => '', // Allow any certificate + ], + default => [], + }; + $connection = match ($driver) { null, 'sqlite' => [ 'driver' => 'pdo_sqlite', 'path' => 'data/database.sqlite', ], default => [ - 'driver' => $resolveDriver(), + 'driver' => $doctrineDriver, 'dbname' => EnvVars::DB_NAME->loadFromEnv(), 'user' => $readCredentialAsString(EnvVars::DB_USER), 'password' => $readCredentialAsString(EnvVars::DB_PASSWORD), 'host' => EnvVars::DB_HOST->loadFromEnv(), 'port' => EnvVars::DB_PORT->loadFromEnv(), 'unix_socket' => $isMysqlCompatible ? EnvVars::DB_UNIX_SOCKET->loadFromEnv() : null, - 'charset' => $resolveCharset(), - 'driverOptions' => $driver !== 'mssql' ? [] : [ - 'TrustServerCertificate' => 'true', - ], + 'charset' => $charset, + 'driverOptions' => $driverOptions, ], }; @@ -63,7 +73,7 @@ Events::postFlush => [ShortUrlVisitsCountTracker::class, OrphanVisitsCountTracker::class], ], ], - 'connection' => $resolveConnection(), + 'connection' => $connection, ], ]; diff --git a/module/Core/src/Config/EnvVars.php b/module/Core/src/Config/EnvVars.php index e2a6d38ad..f8042febd 100644 --- a/module/Core/src/Config/EnvVars.php +++ b/module/Core/src/Config/EnvVars.php @@ -36,6 +36,7 @@ enum EnvVars: string case DB_HOST = 'DB_HOST'; case DB_UNIX_SOCKET = 'DB_UNIX_SOCKET'; case DB_PORT = 'DB_PORT'; + case DB_USE_ENCRYPTION = 'DB_USE_ENCRYPTION'; case GEOLITE_LICENSE_KEY = 'GEOLITE_LICENSE_KEY'; case CACHE_NAMESPACE = 'CACHE_NAMESPACE'; case REDIS_SERVERS = 'REDIS_SERVERS'; @@ -147,6 +148,7 @@ private function defaultValue(): string|int|bool|null 'mssql' => '1433', default => '3306', }, + self::DB_USE_ENCRYPTION => false, self::MERCURE_INTERNAL_HUB_URL => self::MERCURE_PUBLIC_HUB_URL->loadFromEnv(), From c34bfac6b112b146ee88ddedd8ec42a18c62c3c4 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 20 Dec 2024 09:29:28 +0100 Subject: [PATCH 2/3] Update installer with support for DB_USE_ENCRYPTION option --- CHANGELOG.md | 1 + composer.json | 2 +- config/autoload/installer.global.php | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 25c508a8b..b3cdeab8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this All [URI-reserved characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2.2) were disallowed up until now, but from now on, only the gen-delimiters are. * [#2229](https://github.com/shlinkio/shlink/issues/2229) Add `logo=disabled` query param to dynamically disable the default logo on QR codes. +* [#2206](https://github.com/shlinkio/shlink/issues/2206) Add new `DB_USE_ENCRYPTION` config option to enable SSL database connections trusting any server certificate. ### Changed * [#2281](https://github.com/shlinkio/shlink/issues/2281) Update docker image to PHP 8.4 diff --git a/composer.json b/composer.json index 9fe15aeac..aa1a1e526 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "shlinkio/shlink-config": "^3.4", "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", - "shlinkio/shlink-installer": "dev-develop#957db97 as 9.4", + "shlinkio/shlink-installer": "dev-develop#3675f6d as 9.4", "shlinkio/shlink-ip-geolocation": "^4.2", "shlinkio/shlink-json": "^1.1", "spiral/roadrunner": "^2024.1", diff --git a/config/autoload/installer.global.php b/config/autoload/installer.global.php index e239dd3a1..24b9263e4 100644 --- a/config/autoload/installer.global.php +++ b/config/autoload/installer.global.php @@ -20,6 +20,7 @@ Option\Database\DatabaseUserConfigOption::class, Option\Database\DatabasePasswordConfigOption::class, Option\Database\DatabaseUnixSocketConfigOption::class, + Option\Database\DatabaseUseEncryptionConfigOption::class, Option\UrlShortener\ShortDomainHostConfigOption::class, Option\UrlShortener\ShortDomainSchemaConfigOption::class, Option\Redirect\BaseUrlRedirectConfigOption::class, From d228c16f8260d8d93535cccae212b640b4fba720 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Fri, 20 Dec 2024 09:52:30 +0100 Subject: [PATCH 3/3] Fix test for ip middleware --- composer.json | 2 +- module/Core/test-api/Action/RedirectTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index aa1a1e526..ca6cab077 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", - "akrabat/ip-address-middleware": "^2.4", + "akrabat/ip-address-middleware": "^2.5", "cakephp/chronos": "^3.1", "doctrine/dbal": "^4.2", "doctrine/migrations": "^3.8", diff --git a/module/Core/test-api/Action/RedirectTest.php b/module/Core/test-api/Action/RedirectTest.php index 79a13fbf4..2cfe9417a 100644 --- a/module/Core/test-api/Action/RedirectTest.php +++ b/module/Core/test-api/Action/RedirectTest.php @@ -93,7 +93,7 @@ public static function provideRequestOptions(): iterable foreach ($ipAddressConfig['rka']['ip_address']['headers_to_inspect'] as $header) { yield sprintf('rule: IP address in "%s" header', $header) => [ [ - RequestOptions::HEADERS => [$header => '1.2.3.4'], + RequestOptions::HEADERS => [$header => $header !== 'Forwarded' ? '1.2.3.4' : 'for=1.2.3.4'], ], 'https://example.com/static-ip-address', ];