diff --git a/assets/apps-app-url-verification-warning.png b/assets/apps-app-url-verification-warning.png new file mode 100644 index 0000000000..1f0745e0f3 Binary files /dev/null and b/assets/apps-app-url-verification-warning.png differ diff --git a/guides/hosting/configurations/shopware/environment-variables.md b/guides/hosting/configurations/shopware/environment-variables.md index cd4cd4fa99..9218dc2483 100644 --- a/guides/hosting/configurations/shopware/environment-variables.md +++ b/guides/hosting/configurations/shopware/environment-variables.md @@ -18,7 +18,7 @@ This page lists all environment variables that can be used to configure Shopware | `APP_LOG_DIR` | `{projectRoot}/var/log` | Path to a directory to store logs (since 6.6.8.0) | | `APP_SECRET` | (empty) | Can be generated with `openssl rand -hex 32` | | `APP_URL` | (empty) | Where Shopware will be accessible | -| `APP_URL_CHECK_DISABLED` | `false` | Disable URL validation checks | +| `APP_URL_CHECK_DISABLED` | `false` | Hides the Administration warning about an incorrect `APP_URL`. App installation and secret rotation still check `APP_URL`. | | `BLUE_GREEN_DEPLOYMENT` | `0` | This needs super privilege to create trigger | | `COMPOSER_HOME` | `/tmp/composer` | Caching for the Plugin Manager | | `COMPOSER_PLUGIN_LOADER` | (empty) | When set to a non-empty value (e.g., `1` or `true`), enables the Composer plugin loader instead of the database plugin loader. All plugins defined in the root `composer.json` will be automatically active, regardless of database settings. | diff --git a/guides/hosting/installation-updates/cluster-setup.md b/guides/hosting/installation-updates/cluster-setup.md index 953e77cffb..ddedbb358c 100644 --- a/guides/hosting/installation-updates/cluster-setup.md +++ b/guides/hosting/installation-updates/cluster-setup.md @@ -64,11 +64,13 @@ To use the composer plugin loader, add the environment variable `COMPOSER_PLUGIN We recommend setting up at least five Redis servers for the following resources: 1. [Session](../performance/session) + [cart](../performance/cart-storage#using-redis-as-storage) -2. [cache.object](../performance/caches#using-redis) +2. [cache.object](../performance/caches#using-redis) + `cache.app_url_verification` 3. [Lock](../performance/lock-store) + [Increment storage](../performance/increment) 4. [Number Ranges](../performance/number-ranges) 5. [Message Queue](../infrastructure/message-queue) +The `cache.app_url_verification` pool must be shared between all nodes, otherwise apps cannot be installed; see [APP_URL Verification](../../plugins/apps/lifecycle/app-url-verification.md#several-nodes). + The PHP Redis extension provides persistent Redis connections. Persistent connections can help in high load scenarios as each request doesn't have to open and close connections. Using non-persistent Redis connections can also hit the system's maximum open sockets. diff --git a/guides/hosting/installation-updates/creating-a-staging-instance.md b/guides/hosting/installation-updates/creating-a-staging-instance.md index 5082e2675d..7186b4405e 100644 --- a/guides/hosting/installation-updates/creating-a-staging-instance.md +++ b/guides/hosting/installation-updates/creating-a-staging-instance.md @@ -190,6 +190,8 @@ Protect your staging environment from unauthorized access using password protect The simplest method uses `.htaccess` for Apache or `auth_basic` for Nginx. You can also use a firewall to restrict access by IP address. +Shopware must still be able to reach itself under `APP_URL`, otherwise installing or reinstalling apps fails; see [APP_URL Verification](../../plugins/apps/lifecycle/app-url-verification.md#protected-installations). + **Apache example:** ```apache diff --git a/guides/hosting/performance/performance-tweaks.md b/guides/hosting/performance/performance-tweaks.md index ebf627991a..60a995e9d9 100644 --- a/guides/hosting/performance/performance-tweaks.md +++ b/guides/hosting/performance/performance-tweaks.md @@ -251,11 +251,6 @@ monolog: The `business_event_handler_buffer` handler logs flow. Setting it to `error` will disable the logging of flow activities that succeed. -## Disable App URL external check - -On any Administration load, Shopware tries to request itself to test that the configured `APP_URL` inside `.env` is correct. -If your `APP_URL` is correct, you can disable this behavior with an environment variable `APP_URL_CHECK_DISABLED=1`. - ## Using zstd instead of gzip for compression Shopware uses `gzip` for compressing the cache elements and the cart when enabled. diff --git a/guides/plugins/apps/lifecycle/app-registration-setup.md b/guides/plugins/apps/lifecycle/app-registration-setup.md index ae614cc2e8..f3a21af1ec 100644 --- a/guides/plugins/apps/lifecycle/app-registration-setup.md +++ b/guides/plugins/apps/lifecycle/app-registration-setup.md @@ -49,6 +49,9 @@ If no SDK is available for your language, you can implement the registration pro The registration request is made via a `GET` request to the URL you provide in your app's manifest file. +Before sending it, Shopware verifies that its `APP_URL` reaches this installation. +If the verification fails, the request is not sent; see [APP_URL Verification](app-url-verification.md). + <<< @/docs/snippets/config/app/setup.xml The following query parameters will be sent with the request: @@ -521,3 +524,6 @@ This means that this installation will get a new shopId, which is used during re As the new installation will get a new shopId, the installed apps will continue working on the old installation as before, but, as a consequence, the data on the app's side associated with the old shopId cannot be accessed on the new installation. * **UninstallApps**: This strategy will simply uninstall all apps on the new installation, thus keeping the old installation working like before. + +Before MoveShopPermanently or ReinstallApps registers the apps again, Shopware verifies that `APP_URL` reaches this installation. +If the [verification](app-url-verification.md) fails, the strategy stops and the shop ID stays unchanged. diff --git a/guides/plugins/apps/lifecycle/app-url-verification.md b/guides/plugins/apps/lifecycle/app-url-verification.md new file mode 100644 index 0000000000..90df38eeb7 --- /dev/null +++ b/guides/plugins/apps/lifecycle/app-url-verification.md @@ -0,0 +1,128 @@ +--- +nav: + title: APP_URL Verification + position: 42 + +--- + +# APP_URL Verification + +## Overview + +Before Shopware registers the shop with an app server, it verifies that `APP_URL` leads back to this installation. +A wrong `APP_URL` would otherwise give the app server the address of another shop, for example the live shop on a staging copy, or of no shop at all. +If the verification fails, Shopware does not contact the app server. + +::: info +This page is for shop operators. +For the registration protocol on the app side, see [App Registration & Backend Setup](app-registration-setup.md). +::: + +## How the verification works + +1. Shopware creates a random token and stores it in the `cache.app_url_verification` cache pool for two minutes. +2. Shopware sends a `GET` request to `{APP_URL}/api/app-system/shop/verify` with the token. + The request leaves the server and comes back in the same way as any visitor's request: through DNS, firewalls, proxies, and any load balancer. +3. The Shopware installation that receives the request looks up the token in the cache. + It answers `204 No Content` if the token matches, and `400 Bad Request` if it does not. + +```mermaid +flowchart LR + A[Shop stores token] --> B[Request goes out to APP_URL] + B --> C{Where does it arrive?} + C -->|Back at this shop| D[Token found: pass] + C -->|Another server| E[Token not found: hard fail] +``` + +The request times out after two seconds and follows at most one redirect. + +## When the verification runs + +Shopware verifies `APP_URL` every time it registers with an app server, in every environment: + +- when an app with a backend, or a Shopware service, is installed +- when an update adds a backend to an app that had none +- when an app secret is rotated with `bin/console app:secret:rotate`, or recovered by running `bin/console app:install` again +- when the `move-shop-permanently` or `reinstall-apps` strategy of `bin/console app:shop-id:change` runs, before the shop ID changes + +If the verification fails, the installation, update, secret rotation, or strategy stops. +A shop ID change strategy then leaves the shop ID unchanged. + +Apps without a backend do not register with an app server, so they are never verified. + +## Results + +| Result | When | Error during registration | +| :-------- | :------------------------------------------------------------------------------------------------------------------------------------------------------ | :------------------------------------------------------- | +| Pass | The request returned `204` | None, the registration continues | +| Soft fail | No connection, for example a DNS error or a timeout, or an HTTP `5xx` or `429` answer | `FRAMEWORK__APP_URL_VERIFICATION_UNAVAILABLE` (HTTP 503) | +| Hard fail | Any other answer, for example `400` from an installation that does not know the token, or `401`/`403` from a login or firewall, or an invalid `APP_URL` | `FRAMEWORK__APP_URL_FAILED_VERIFICATION` (HTTP 502) | + +A soft fail is usually temporary, so try again later. +A hard fail means that `APP_URL` does not reach this installation, and it has to be fixed before apps can register. + +## Requirements + +### The shop must reach itself + +The server or container that runs Shopware must be able to resolve and reach `APP_URL`. +This includes setups where `APP_URL` only works from outside, for example a Docker container whose `APP_URL` points to a port that is only mapped on the host. + +### Protected installations + +Staging instances are often protected by a login or an IP restriction. +The verification request has no login and comes from the server itself. + +- With an IP restriction, add the public IP address the server sends requests from to the allowlist. +- With password protection or an authentication proxy, exclude `/api/app-system/shop/verify`, or all of `/api`. + +See [Creating a staging instance](../../../hosting/installation-updates/creating-a-staging-instance.md#protecting-the-staging-environment) for an Apache example. + +### Several nodes + +The token is stored on the node that starts the verification, but the request can reach any node behind the load balancer. +The `cache.app_url_verification` pool must therefore be shared between all nodes. + +The pool uses `cache.app`. +If `cache.app` is already shared, for example on Redis as described in [Cache](../../../hosting/performance/caches.md#using-redis), nothing else is needed. +Otherwise, point the pool to a shared adapter: + +```yaml +# config/packages/cache.yaml +framework: + cache: + pools: + cache.app_url_verification: + adapter: cache.adapter.redis + provider: redis://localhost +``` + +### Rate limit + +The verification endpoint accepts 60 failed requests per IP address per hour. +A successful verification resets the counter. +When the limit is reached, the endpoint answers `429`, which is a soft fail. + +## Commands + +- `bin/console app:url:status` shows the result of the last verification without running a new one: the checked URL, the result, the reason, the number of attempts, and the time of the check. +- `bin/console app:url:verify` runs a new verification, stores its result, and shows it in the same format. + If a shop ID change is pending, it shows a warning and still verifies `APP_URL`. + +Run `bin/console app:url:verify` after fixing `APP_URL`, so that the stored result is up to date. + +## Administration warning + +In the `prod` environment, the Administration shows a warning when the stored result is a hard fail and apps with a backend are installed. +The warning shows the checked `APP_URL`, the HTTP status code, and the command to run. + +![APP_URL verification warning in the Administration](../../../../assets/apps-app-url-verification-warning.png) + +The Administration reuses the stored result: + +- A pass is reused for 24 hours. +- After a soft fail, Shopware tries again with increasing pauses, from one minute up to one hour, and shows no warning. +- A hard fail is kept until the next verification, so the warning stays until `bin/console app:url:verify` runs or an app registers successfully. + +Setting `APP_URL_CHECK_DISABLED=1` hides the warning. +It does not turn off the verification during app registration. diff --git a/resources/references/core-reference/commands-reference.md b/resources/references/core-reference/commands-reference.md index 1b42bc0957..a85453998b 100644 --- a/resources/references/core-reference/commands-reference.md +++ b/resources/references/core-reference/commands-reference.md @@ -44,6 +44,8 @@ $ bin/console [command] [parameters] | `app:secret:rotate` | Rotate the shared app secret and integration credentials for one or all apps | | `app:shop-id:change` | \[app:url-change:resolve\] Change the shop ID by choosing a resolution strategy | | `app:uninstall` | Uninstalls the app | +| `app:url:status` | Shows the result of the last `APP_URL` verification | +| `app:url:verify` | Verifies `APP_URL` and shows the result | | `app:validate` | Checks manifests for errors | ### Assets