From f4ada97d1af1c57221ded422538c984d9958f2e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Sep 2023 17:02:07 +0200 Subject: [PATCH 1/7] Allow any hostname, and serve API+Console by default --- app/config/roles.php | 2 -- app/controllers/general.php | 21 ++++++++------------- app/controllers/web/console.php | 7 ------- app/http.php | 21 ++++++++------------- 4 files changed, 16 insertions(+), 35 deletions(-) diff --git a/app/config/roles.php b/app/config/roles.php index abc00b55bf..ee96f24e48 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -3,7 +3,6 @@ use Appwrite\Auth\Auth; $member = [ - 'global', 'public', 'home', 'console', @@ -24,7 +23,6 @@ $member = [ ]; $admins = [ - 'global', 'graphql', 'teams.read', 'teams.write', diff --git a/app/controllers/general.php b/app/controllers/general.php index e3b47098b2..1e305c3107 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -47,8 +47,6 @@ Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleRequest, Request $request, Response $response) { - $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); - $host = $request->getHostname() ?? ''; $route = Authorization::skip( @@ -59,15 +57,12 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques )[0] ?? null; if ($route === null) { - $mainDomain = App::getEnv('_APP_DOMAIN', ''); - - if ($mainDomain === 'localhost') { - throw new AppwriteException(AppwriteException::ROUTER_DOMAIN_NOT_CONFIGURED); - } else { - throw new AppwriteException(AppwriteException::ROUTER_HOST_NOT_FOUND); - } + // Act as API - no Proxy logic + return false; } + $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); + $projectId = $route->getAttribute('projectId'); $project = Authorization::skip( fn () => $dbForConsole->getDocument('projects', $projectId) @@ -193,7 +188,7 @@ App::init() $host = $request->getHostname() ?? ''; $mainDomain = App::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain - if ($host !== $mainDomain && $host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { + if ($host !== $mainDomain) { if (router($utopia, $dbForConsole, $swooleRequest, $request, $response)) { return; } @@ -327,7 +322,7 @@ App::init() * @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers */ if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS - if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // Localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations + if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== APP_HOSTNAME_INTERNAL) { // APP_HOSTNAME_INTERNAL allowed for migrations if ($request->getMethod() !== Request::METHOD_GET) { throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP.'); } @@ -497,7 +492,7 @@ App::options() $host = $request->getHostname() ?? ''; $mainDomain = App::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain - if ($host !== $mainDomain && $host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { + if ($host !== $mainDomain) { if (router($utopia, $dbForConsole, $swooleRequest, $request, $response)) { return; } @@ -763,7 +758,7 @@ include_once __DIR__ . '/shared/api/auth.php'; App::wildcard() ->groups(['api']) - ->label('scope', 'global') + ->label('scope', 'public') ->action(function () { throw new AppwriteException(AppwriteException::GENERAL_ROUTE_NOT_FOUND); }); diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index d7496b03bd..4a6f15df3a 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -31,13 +31,6 @@ App::get('/console/*') ->inject('request') ->inject('response') ->action(function (Request $request, Response $response) { - // Serve static files (console) only for main domain - $host = $request->getHostname() ?? ''; - $mainDomain = App::getEnv('_APP_DOMAIN', ''); - if ($host !== $mainDomain && $host !== 'localhost' && $host !== APP_HOSTNAME_INTERNAL) { - throw new Exception(Exception::GENERAL_ROUTE_NOT_FOUND); - } - $fallback = file_get_contents(__DIR__ . '/../../../console/index.html'); // Card SSR diff --git a/app/http.php b/app/http.php index 053db20ffc..fe1ed48724 100644 --- a/app/http.php +++ b/app/http.php @@ -229,21 +229,16 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $request = new Request($swooleRequest); $response = new Response($swooleResponse); - // Serve static files (console) only for main domain - $host = $request->getHostname() ?? ''; - $mainDomain = App::getEnv('_APP_DOMAIN', ''); - if ($host === $mainDomain || $host === 'localhost') { - if (Files::isFileLoaded($request->getURI())) { - $time = (60 * 60 * 24 * 365 * 2); // 45 days cache + if (Files::isFileLoaded($request->getURI())) { + $time = (60 * 60 * 24 * 365 * 2); // 45 days cache - $response - ->setContentType(Files::getFileMimeType($request->getURI())) - ->addHeader('Cache-Control', 'public, max-age=' . $time) - ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache - ->send(Files::getFileContents($request->getURI())); + $response + ->setContentType(Files::getFileMimeType($request->getURI())) + ->addHeader('Cache-Control', 'public, max-age=' . $time) + ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache + ->send(Files::getFileContents($request->getURI())); - return; - } + return; } $app = new App('UTC'); From cda58d225689f7117ec915aa20700015a6a44974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Tue, 5 Sep 2023 17:09:54 +0200 Subject: [PATCH 2/7] Re-add automatic ssl generation --- app/controllers/general.php | 55 +++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index 1e305c3107..01f88b7b67 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -229,6 +229,61 @@ App::init() Request::setFilter(null); } + $domain = $request->getHostname(); + $domains = Config::getParam('domains', []); + if (!array_key_exists($domain, $domains)) { + $domain = new Domain(!empty($domain) ? $domain : ''); + + if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) { + $domains[$domain->get()] = false; + Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.'); + } elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) { + Console::warning('Skipping SSL certificates generation on ACME challenge.'); + } else { + Authorization::disable(); + + $envDomain = App::getEnv('_APP_DOMAIN', ''); + $mainDomain = null; + if (!empty($envDomain) && $envDomain !== 'localhost') { + $mainDomain = $envDomain; + } else { + $domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('_id')]); + $mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get(); + } + + if ($mainDomain !== $domain->get()) { + Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.'); + } else { + $domainDocument = $dbForConsole->findOne('rules', [ + Query::equal('domain', [$domain->get()]) + ]); + + if (!$domainDocument) { + $domainDocument = new Document([ + 'domain' => $domain->get(), + 'resourceType' => 'api', + 'status' => 'verifying', + 'projectId' => 'console', + 'projectInternalId' => 0 + ]); + + $domainDocument = $dbForConsole->createDocument('rules', $domainDocument); + + Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...'); + + (new Certificate()) + ->setDomain($domainDocument) + ->setSkipRenewCheck(true) + ->trigger(); + } + } + $domains[$domain->get()] = true; + + Authorization::reset(); // ensure authorization is re-enabled + } + Config::setParam('domains', $domains); + } + $localeParam = (string) $request->getParam('locale', $request->getHeader('x-appwrite-locale', '')); if (\in_array($localeParam, $localeCodes)) { $locale->setDefault($localeParam); From cafe7928796102b024c74451ab20e408b6a0bdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Sep 2023 09:34:02 +0200 Subject: [PATCH 3/7] Fix API returning html --- app/controllers/general.php | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/general.php b/app/controllers/general.php index 01f88b7b67..5c1a23d4a9 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -158,6 +158,7 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques return true; } elseif ($type === 'api') { + $utopia->getRoute()?->label('error', ''); return false; } else { throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); From 0d6abda431e2385d612ada6f635f31888d245bfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Sep 2023 10:27:21 +0200 Subject: [PATCH 4/7] Fix autossl domain query --- app/controllers/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 5c1a23d4a9..17bb390d82 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -248,7 +248,7 @@ App::init() if (!empty($envDomain) && $envDomain !== 'localhost') { $mainDomain = $envDomain; } else { - $domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('_id')]); + $domainDocument = $dbForConsole->findOne('rules', [Query::orderAsc('$id')]); $mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get(); } From adb4c72a9c499132be21dac2d5353c8ef4987126 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Sep 2023 10:31:40 +0200 Subject: [PATCH 5/7] Fix project internal ID --- app/controllers/general.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/general.php b/app/controllers/general.php index 17bb390d82..21260e050d 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -265,7 +265,7 @@ App::init() 'resourceType' => 'api', 'status' => 'verifying', 'projectId' => 'console', - 'projectInternalId' => 0 + 'projectInternalId' => 'console' ]); $domainDocument = $dbForConsole->createDocument('rules', $domainDocument); From 0d4957009f0e493139ef69e49480e50558f21335 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Sep 2023 10:42:01 +0200 Subject: [PATCH 6/7] Fix bug with autossl generator --- app/workers/certificates.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 5a1e173b3a..9202b369d1 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -438,6 +438,12 @@ class CertificatesV1 extends Worker $this->dbForConsole->updateDocument('rules', $rule->getId(), $rule); $projectId = $rule->getAttribute('projectId'); + + // Skip events for console project (triggered by auto-ssl generation for 1 click setups) + if($projectId === 'console') { + return; + } + $project = $this->dbForConsole->getDocument('projects', $projectId); /** Trigger Webhook */ From ed588a64756d18db38876fc1662549568b7ef7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 6 Sep 2023 18:45:22 +0200 Subject: [PATCH 7/7] linter fix --- app/workers/certificates.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/workers/certificates.php b/app/workers/certificates.php index 9202b369d1..d390ed442e 100644 --- a/app/workers/certificates.php +++ b/app/workers/certificates.php @@ -440,7 +440,7 @@ class CertificatesV1 extends Worker $projectId = $rule->getAttribute('projectId'); // Skip events for console project (triggered by auto-ssl generation for 1 click setups) - if($projectId === 'console') { + if ($projectId === 'console') { return; }