From 9c320f9c71c504c34f5c8254cf0a2146676f2b6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Aug 2025 10:23:39 +0200 Subject: [PATCH] Revert "Check CAA record before issuing certificate" --- .env | 2 - app/config/variables.php | 18 ------ app/controllers/api/console.php | 2 - app/controllers/api/proxy.php | 13 ---- app/views/install/compose.phtml | 8 --- composer.json | 1 - composer.lock | 62 +------------------ docker-compose.yml | 8 --- src/Appwrite/Network/Validator/DNS.php | 58 +++++++---------- .../Platform/Workers/Certificates.php | 13 ---- .../Response/Model/ConsoleVariables.php | 42 ++++++------- .../Console/ConsoleConsoleClientTest.php | 3 +- tests/unit/Network/Validators/DNSTest.php | 27 -------- 13 files changed, 46 insertions(+), 211 deletions(-) diff --git a/.env b/.env index 4d7c038a6b..76af83a946 100644 --- a/.env +++ b/.env @@ -21,7 +21,6 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_ROUTER_FORCE_HTTPS=disabled _APP_OPENSSL_KEY_V1=your-secret-key -_APP_DNS=8.8.8.8 _APP_DOMAIN=traefik _APP_CONSOLE_DOMAIN=localhost _APP_DOMAIN_FUNCTIONS=functions.localhost @@ -29,7 +28,6 @@ _APP_DOMAIN_SITES=sites.localhost _APP_DOMAIN_TARGET_CNAME=test.localhost _APP_DOMAIN_TARGET_A=127.0.0.1 _APP_DOMAIN_TARGET_AAAA=::1 -_APP_DOMAIN_TARGET_CAA=digicert.com _APP_RULES_FORMAT=md5 _APP_REDIS_HOST=redis _APP_REDIS_PORT=6379 diff --git a/app/config/variables.php b/app/config/variables.php index 8fd00557b3..71ed13a483 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -151,24 +151,6 @@ return [ 'question' => '', 'filter' => '' ], - [ - 'name' => '_APP_DOMAIN_TARGET_CAA', - 'description' => 'A CAA record domain that can be used to validate custom domains. Value should be domain\'s hostname.', - 'introduction' => '', - 'default' => '', - 'required' => false, - 'question' => '', - 'filter' => '' - ], - [ - 'name' => '_APP_DNS', - 'description' => 'DNS server to use for domain validation. Default: 8.8.8.8', - 'introduction' => '', - 'default' => '8.8.8.8', - 'required' => false, - 'question' => '', - 'filter' => '' - ], [ 'name' => '_APP_CONSOLE_WHITELIST_ROOT', 'description' => 'This option allows you to disable the creation of new users on the Appwrite console. When enabled only 1 user will be able to use the registration form. New users can be added by inviting them to your project. By default this option is enabled.', diff --git a/app/controllers/api/console.php b/app/controllers/api/console.php index b0619df3b3..558dc0e4ef 100644 --- a/app/controllers/api/console.php +++ b/app/controllers/api/console.php @@ -71,8 +71,6 @@ App::get('/v1/console/variables') '_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'), '_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'), '_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'), - // Combine CAA domain with most common flags and tag (no parameters) - '_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"', '_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'), '_APP_COMPUTE_SIZE_LIMIT' => +System::getEnv('_APP_COMPUTE_SIZE_LIMIT'), '_APP_USAGE_STATS' => System::getEnv('_APP_USAGE_STATS'), diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index 6b76b0c34c..417ea602ba 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -286,19 +286,6 @@ App::patch('/v1/proxy/rules/:ruleId/verification') throw new Exception(Exception::RULE_VERIFICATION_FAILED); } - // Ensure CAA won't block certificate issuance - if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { - $validationStart = \microtime(true); - $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); - if (!$validator->isValid($domain->get())) { - $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - $error = $validator->getDescription(); - $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); - throw new Exception(Exception::RULE_VERIFICATION_FAILED, 'Domain verification failed because CAA records do not allow certainly.com to issue certificates.'); - } - } - $dbForPlatform->updateDocument('rules', $rule->getId(), $rule->setAttribute('status', 'verifying')); // Issue a TLS certificate when domain is verified diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 34940e7c6e..89facfe0f1 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -95,8 +95,6 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DOMAINS_DNS - _APP_DOMAIN_FUNCTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -474,8 +472,6 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DOMAINS_DNS - _APP_DOMAIN_FUNCTIONS - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST @@ -633,8 +629,6 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DOMAINS_DNS - _APP_EMAIL_SECURITY - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -666,8 +660,6 @@ $image = $this->getParam('image', ''); - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DOMAINS_DNS - _APP_DOMAIN_FUNCTIONS - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/composer.json b/composer.json index 8ba8e49f4a..73cdcc3d86 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,6 @@ "utopia-php/database": "0.71.*", "utopia-php/detector": "0.1.*", "utopia-php/domains": "0.8.*", - "utopia-php/dns": "0.3.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", "utopia-php/fetch": "0.4.*", diff --git a/composer.lock b/composer.lock index 88356d5349..aafe1d216c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "761a7e17b49381e68038c92873888125", + "content-hash": "7b2ef6192403daf5c492219822ce0aa1", "packages": [ { "name": "adhocore/jwt", @@ -3641,62 +3641,6 @@ }, "time": "2025-05-19T11:01:28+00:00" }, - { - "name": "utopia-php/dns", - "version": "0.3.0", - "source": { - "type": "git", - "url": "https://github.com/utopia-php/dns.git", - "reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/8fd4161bc3a8021a670c1101b40f6b09a97f1a54", - "reference": "8fd4161bc3a8021a670c1101b40f6b09a97f1a54", - "shasum": "" - }, - "require": { - "php": ">=8.0", - "utopia-php/cli": "0.15.*", - "utopia-php/telemetry": "^0.1.1" - }, - "require-dev": { - "laravel/pint": "1.2.*", - "phpstan/phpstan": "1.8.*", - "phpunit/phpunit": "^9.3", - "rregeer/phpunit-coverage-check": "^0.3.1", - "swoole/ide-helper": "4.6.6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Utopia\\DNS\\": "src/DNS" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Eldad Fux", - "email": "eldad@appwrite.io" - } - ], - "description": "Lite & fast micro PHP DNS server abstraction that is **easy to use**.", - "keywords": [ - "dns", - "framework", - "php", - "upf", - "utopia" - ], - "support": { - "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/0.3.0" - }, - "time": "2025-08-04T11:05:53+00:00" - }, { "name": "utopia-php/domains", "version": "0.8.0", @@ -8364,7 +8308,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { @@ -8388,5 +8332,5 @@ "platform-overrides": { "php": "8.3" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.6.0" } diff --git a/docker-compose.yml b/docker-compose.yml index 0e299c8a2c..58b78fcd8e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -120,8 +120,6 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -537,8 +535,6 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_EMAIL_CERTIFICATES - _APP_REDIS_HOST @@ -708,8 +704,6 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DNS - _APP_EMAIL_SECURITY - _APP_REDIS_HOST - _APP_REDIS_PORT @@ -744,8 +738,6 @@ services: - _APP_DOMAIN_TARGET_CNAME - _APP_DOMAIN_TARGET_AAAA - _APP_DOMAIN_TARGET_A - - _APP_DOMAIN_TARGET_CAA - - _APP_DNS - _APP_DOMAIN_FUNCTIONS - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST diff --git a/src/Appwrite/Network/Validator/DNS.php b/src/Appwrite/Network/Validator/DNS.php index 7549d18f54..73494ddc3e 100644 --- a/src/Appwrite/Network/Validator/DNS.php +++ b/src/Appwrite/Network/Validator/DNS.php @@ -2,16 +2,13 @@ namespace Appwrite\Network\Validator; -use Utopia\DNS\Client; -use Utopia\System\System; use Utopia\Validator; class DNS extends Validator { - public const RECORD_A = 'A'; - public const RECORD_AAAA = 'AAAA'; - public const RECORD_CNAME = 'CNAME'; - public const RECORD_CAA = 'CAA'; // You can provide domain only (as $target) for CAA validation + public const RECORD_A = 'a'; + public const RECORD_AAAA = 'aaaa'; + public const RECORD_CNAME = 'cname'; /** * @var mixed @@ -45,49 +42,42 @@ class DNS extends Validator * Check if DNS record value matches specific value * * @param mixed $domain + * * @return bool */ public function isValid($value): bool { + $typeNative = match ($this->type) { + self::RECORD_A => DNS_A, + self::RECORD_AAAA => DNS_AAAA, + self::RECORD_CNAME => DNS_CNAME, + default => throw new \Exception('Record type not supported.') + }; + + $dnsKey = match ($this->type) { + self::RECORD_A => 'ip', + self::RECORD_AAAA => 'ipv6', + self::RECORD_CNAME => 'target', + default => throw new \Exception('Record type not supported.') + }; + if (!is_string($value)) { return false; } - $dnsServer = System::getEnv('_APP_DNS', '8.8.8.8'); - $dns = new Client($dnsServer); - try { - $query = $dns->query($value, $this->type); - $this->logs = $query; - } catch (\Exception $e) { - $this->logs = ['error' => $e->getMessage()]; + $records = \dns_get_record($value, $typeNative); + $this->logs = $records; + } catch (\Throwable $th) { return false; } - if (empty($query)) { - // No CAA records means anyone can issue certificate - if ($this->type === self::RECORD_CAA) { - return true; - } - + if (!$records) { return false; } - foreach ($query as $record) { - // CAA validation only needs to ensure domain - if ($this->type === self::RECORD_CAA) { - // Extract domain; comments showcase extraction steps in most complex scenario - $rdata = $record->getRdata(); // 255 issuewild "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600" - $rdata = \explode(' ', $rdata, 3)[2] ?? ''; // "certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600" - $rdata = \trim($rdata, '"'); // certainly.com;validationmethods=tls-alpn-01;retrytimeout=3600 - $rdata = \explode(';', $rdata, 2)[0] ?? ''; // certainly.com - - if ($rdata === $this->target) { - return true; - } - } - - if ($record->getRdata() === $this->target) { + foreach ($records as $record) { + if (isset($record[$dnsKey]) && $record[$dnsKey] === $this->target) { return true; } } diff --git a/src/Appwrite/Platform/Workers/Certificates.php b/src/Appwrite/Platform/Workers/Certificates.php index d14bf0428d..207f95ff7d 100644 --- a/src/Appwrite/Platform/Workers/Certificates.php +++ b/src/Appwrite/Platform/Workers/Certificates.php @@ -337,19 +337,6 @@ class Certificates extends Action throw new Exception('Failed to verify domain DNS records.'); } - - // Ensure CAA won't block certificate issuance - if (!empty(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''))) { - $validationStart = \microtime(true); - $validator = new DNS(System::getEnv('_APP_DOMAIN_TARGET_CAA', ''), DNS::RECORD_CAA); - if (!$validator->isValid($domain->get())) { - $log->addExtra('dnsTimingCaa', \strval(\microtime(true) - $validationStart)); - $log->addTag('dnsDomain', $domain->get()); - $error = $validator->getDescription(); - $log->addExtra('dnsResponse', \is_array($error) ? \json_encode($error) : \strval($error)); - throw new Exception('Failed to verify domain DNS records. CAA records do not allow certificates from certainly.com to issue certificates.'); - } - } } else { // Main domain validation // TODO: Would be awesome to check A/AAAA record here. Maybe dry run? diff --git a/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php b/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php index b81502f0a1..97dae2efcd 100644 --- a/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php +++ b/src/Appwrite/Utopia/Response/Model/ConsoleVariables.php @@ -10,30 +10,24 @@ class ConsoleVariables extends Model public function __construct() { $this - ->addRule('_APP_DOMAIN_TARGET_CNAME', [ - 'type' => self::TYPE_STRING, - 'description' => 'CNAME target for your Appwrite custom domains.', - 'default' => '', - 'example' => 'appwrite.io', - ]) - ->addRule('_APP_DOMAIN_TARGET_A', [ - 'type' => self::TYPE_STRING, - 'description' => 'A target for your Appwrite custom domains.', - 'default' => '', - 'example' => '127.0.0.1', - ]) - ->addRule('_APP_DOMAIN_TARGET_AAAA', [ - 'type' => self::TYPE_STRING, - 'description' => 'AAAA target for your Appwrite custom domains.', - 'default' => '', - 'example' => '::1', - ]) - ->addRule('_APP_DOMAIN_TARGET_CAA', [ - 'type' => self::TYPE_STRING, - 'description' => 'CAA target for your Appwrite custom domains.', - 'default' => '', - 'example' => 'digicert.com', - ]) + ->addRule('_APP_DOMAIN_TARGET_CNAME', [ + 'type' => self::TYPE_STRING, + 'description' => 'CNAME target for your Appwrite custom domains.', + 'default' => '', + 'example' => 'appwrite.io', + ]) + ->addRule('_APP_DOMAIN_TARGET_A', [ + 'type' => self::TYPE_STRING, + 'description' => 'A target for your Appwrite custom domains.', + 'default' => '', + 'example' => '127.0.0.1', + ]) + ->addRule('_APP_DOMAIN_TARGET_AAAA', [ + 'type' => self::TYPE_STRING, + 'description' => 'AAAA target for your Appwrite custom domains.', + 'default' => '', + 'example' => '::1', + ]) ->addRule('_APP_STORAGE_LIMIT', [ 'type' => self::TYPE_INTEGER, 'description' => 'Maximum file size allowed for file upload in bytes.', diff --git a/tests/e2e/Services/Console/ConsoleConsoleClientTest.php b/tests/e2e/Services/Console/ConsoleConsoleClientTest.php index 340cabc8c0..6059cb2000 100644 --- a/tests/e2e/Services/Console/ConsoleConsoleClientTest.php +++ b/tests/e2e/Services/Console/ConsoleConsoleClientTest.php @@ -24,11 +24,10 @@ class ConsoleConsoleClientTest extends Scope ], $this->getHeaders())); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertCount(14, $response['body']); + $this->assertCount(13, $response['body']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_CNAME']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_A']); $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_AAAA']); - $this->assertIsString($response['body']['_APP_DOMAIN_TARGET_CAA']); $this->assertIsInt($response['body']['_APP_STORAGE_LIMIT']); $this->assertIsInt($response['body']['_APP_COMPUTE_SIZE_LIMIT']); $this->assertIsBool($response['body']['_APP_DOMAIN_ENABLED']); diff --git a/tests/unit/Network/Validators/DNSTest.php b/tests/unit/Network/Validators/DNSTest.php index 6609a0838a..5e8652381a 100644 --- a/tests/unit/Network/Validators/DNSTest.php +++ b/tests/unit/Network/Validators/DNSTest.php @@ -47,31 +47,4 @@ class DNSTest extends TestCase $this->assertEquals($validator->isValid('aaaa-unit-test.appwrite.org'), true); $this->assertEquals($validator->isValid('test1.appwrite.org'), false); } - - public function testCAA(): void - { - $validator = new DNS('digicert.com', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('github.com'), true); - $this->assertEquals($validator->isValid('test1.appwrite.org'), true); - - $validator = new DNS('0 issue "digicert.com"', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('github.com'), true); - - $validator = new DNS('0 issuewild "digicert.com"', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('github.com'), true); - - $validator = new DNS('128 issue "digicert.com"', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('github.com'), false); - - $validator = new DNS('letsencrypt.org', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('github.com'), false); - - // Valid becasue no CAA record configured - $validator = new DNS('anything.com', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('cloud.appwrite.io'), true); - - // Valid becasue no CAA record configured - $validator = new DNS('something.org', DNS::RECORD_CAA); - $this->assertEquals($validator->isValid('cloud.appwrite.io'), true); - } }