diff --git a/.env b/.env index c1ccc71182..feeb74d849 100644 --- a/.env +++ b/.env @@ -9,6 +9,7 @@ _APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io _APP_SYSTEM_SECURITY_EMAIL_ADDRESS=security@appwrite.io _APP_SYSTEM_RESPONSE_FORMAT= _APP_OPTIONS_ABUSE=disabled +_APP_OPTIONS_ROUTER_PROTECTION=disbled _APP_OPTIONS_FORCE_HTTPS=disabled _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled _APP_OPENSSL_KEY_V1=your-secret-key diff --git a/app/config/variables.php b/app/config/variables.php index 7685f3f735..9d555bf013 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -52,6 +52,15 @@ return [ 'question' => '', 'filter' => '' ], + [ + 'name' => '_APP_OPTIONS_ROUTER_PROTECTION', + 'description' => 'Protects server from serving requests from unknown hostnames, and from serving Console for custom project domains. By default, set to \'disabled\'. To start router protection, set to \'enabled\'. It is recommended to enable this variable on production environment.', + 'introduction' => '1.4.4', + 'default' => 'disabled', + 'required' => false, + 'question' => '', + 'filter' => '' + ], [ 'name' => '_APP_OPENSSL_KEY_V1', 'description' => 'This is your server private secret key that is used to encrypt all sensitive data on your server. Appwrite server encrypts all secret data on your server like webhooks, HTTP passwords, user sessions, and storage files. The var is not set by default, if you wish to take advantage of Appwrite encryption capabilities you should change it and make sure to **keep it a secret and have a backup for it**.', diff --git a/app/controllers/general.php b/app/controllers/general.php index 6da34cf1c2..1222ee6b61 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -47,6 +47,8 @@ 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( @@ -57,12 +59,23 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques )[0] ?? null; if ($route === null) { + if ($host === App::getEnv('_APP_DOMAIN_FUNCTIONS', '')) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain cannot be used for security reasons. Please use any subdomain instead.'); + } + + if (\str_ends_with($host, App::getEnv('_APP_DOMAIN_FUNCTIONS', ''))) { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'This domain is not connected to any Appwrite resource yet. Please configure custom domain or function domain to allow this request.'); + } + + if (App::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled') { + throw new AppwriteException(AppwriteException::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite over this domain. Please add it as custom domain to your project or disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); + } + // Act as API - no Proxy logic + $utopia->getRoute()?->label('error', ''); return false; } - $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); - $projectId = $route->getAttribute('projectId'); $project = Authorization::skip( fn () => $dbForConsole->getDocument('projects', $projectId) @@ -174,6 +187,7 @@ function router(App $utopia, Database $dbForConsole, SwooleRequest $swooleReques throw new AppwriteException(AppwriteException::GENERAL_SERVER_ERROR, 'Unknown resource type ' . $type); } + $utopia->getRoute()?->label('error', ''); return false; } diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php index 4a6f15df3a..01e985fc16 100644 --- a/app/controllers/web/console.php +++ b/app/controllers/web/console.php @@ -28,9 +28,17 @@ App::get('/console/*') ->groups(['web']) ->label('permission', 'public') ->label('scope', 'home') + ->inject('utopia') ->inject('request') ->inject('response') - ->action(function (Request $request, Response $response) { + ->action(function (App $utopia, Request $request, Response $response) { + $host = $request->getHostname() ?? ''; + $mainDomain = App::getEnv('_APP_DOMAIN', ''); + if (App::getEnv('_APP_OPTIONS_ROUTER_PROTECTION', 'disabled') === 'enabled' && $host !== $mainDomain) { + $utopia->getRoute()?->label('error', __DIR__ . '/../../views/general/error.phtml'); + throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Router protection does not allow accessing Appwrite Console over custom domain. Please disable _APP_OPTIONS_ROUTER_PROTECTION environment variable.'); + } + $fallback = file_get_contents(__DIR__ . '/../../../console/index.html'); // Card SSR diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index cbda77d5e2..e75bb3dfa5 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -84,6 +84,7 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 @@ -197,6 +198,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT diff --git a/docker-compose.yml b/docker-compose.yml index 9811015f3a..a18dc83662 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -105,6 +105,7 @@ services: - _APP_SYSTEM_SECURITY_EMAIL_ADDRESS - _APP_SYSTEM_RESPONSE_FORMAT - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1 @@ -223,6 +224,7 @@ services: - _APP_ENV - _APP_WORKER_PER_CORE - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPENSSL_KEY_V1 - _APP_REDIS_HOST - _APP_REDIS_PORT diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index 19e63c5313..bdb9bf49a4 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -66,6 +66,7 @@ services: environment: - _APP_ENV - _APP_OPTIONS_ABUSE + - _APP_OPTIONS_ROUTER_PROTECTION - _APP_OPTIONS_FORCE_HTTPS - _APP_OPTIONS_FUNCTIONS_FORCE_HTTPS - _APP_OPENSSL_KEY_V1