Reverse proxy settings for multisite cannot work

Created on 11 September 2018, over 6 years ago
Updated 20 February 2024, about 1 year ago

Problem/Motivation

Bug:
If you have multiple sites and if your Drupal is behind a reverse proxy, the matching site path is not resolved, the settings are not initialized, and you always end up on the install page (with wrong urls generated).

Why?
1) When the request is handled, one of the first function to be called is DrupalKernel->initializeSettings().
2) To initialize the settings, Drupal needs to determine the site path to read the settings.php file so the functionDrupalKernel::findSitePath() is called.
3) In this function, when you have multiple sites, the function Request->getHttpHost() is called on the request (the http host is needed to match it with the mapping (dir => http host) you have made in the sites.php file).

Problem:
1) The http host depends on the trusted proxies configuration (cf Symfony Request->getHttpHost() code).
2) The trusted proxies are only set by the ReverseProxyMiddleware middleware so they have not yet been set in the request. The container has not yet been initialized.

Note:
This cannot be resolved easily because there is a circular dependency (cf attached schema).
1) To find the site path, Drupal needs the valid http host (so the trusted proxies must have been set on the request).
2) To set the trusted proxies, the settings must have been initiliazed (because currently the reverse proxy settings are in the site settings.php file).
3) To initialize the site settings, Drupal needs the site path.

My two cents:
1) Setting the request trusted proxies in a HttpKernel middleware is a design error.
2) The reverse proxy settings can only be global and not by site. As long as Drupal depends on the http host to find the site, a configuration by site is not possible. I geuss that's ok for the vast majority of project using multiple sites and reverse proxy.

Proposed resolution

Ideas:
1) Set the trusted proxies on the Request as soon as possible. We need to make sure that the trusted proxies are set before any real usage of the Request. The safest solution is to do it at the very beginning of the request handle. If you look at Symfony doc about how to configure Symfony to work behind a reverse proxy (https://symfony.com/doc/current/deployment/proxies.html), they advise the user to set the trusted proxies directly in the front controller (so asap).
2) Deprecate the ReverseProxyMiddleware class.

Proposed patch:
Now:
1) Introduce a new configuration file sites/reverse_proxy_settings.php that behaves exactly like the current reverse proxy settings.
2) Introduce a new method DrupalKernel::setTrustedProxies() that is called as soon as the request is handled.
3) The new method reads the new configuration file and set the trusted proxies on the request.
4) The new method uses the ReverseProxyMiddleware::setSettingsOnRequest() method during the deprecation period (so the same configuration options are used and the behavior is exactly the same).
5) Deprecate the ReverseProxyMiddleware class and trigger a deprecation notice when the reverse proxy settings still come from the site settings.php file (this is the case when you have only one site).

Once the deprecation period is over:
1) Remove the ReverseProxyMiddleware class.
2) Copy the code of the ReverseProxyMiddleware::setSettingsOnRequest() method in the new DrupalKernel::setTrustedProxies() method.
3) Remove the call to ReverseProxyMiddleware::setSettingsOnRequest() in the install.core.inc file as it will not be needed anymore.

Alternatives:
1) Maybe introducing a new configuration file is not the best solution and that the reverse proxy configuration could be in the existing sites.php file directly?
2) Maybe we should remove all reverse proxy configuration from Drupal and and let the users manage the trusted proxy themselves in their front controller (like in Symfony projects).

Remaining tasks

1) Discuss the issue.
2) Review the proposed resolution and patch.
3) Eventually Write tests and update the documentation.

Going further:

Instead of creating a new specific file just for the reverse proxy settings, this could be an opportunity to create a global settings file that all sites settings would inherit. The settings of each site would be merged with the global ones. Those general settings could be used before the site path has been resolved for other purposes. It is already common to create a settings.common.php file that you require in all the sites settings.php files.

๐Ÿ› Bug report
Status

Needs work

Version

11.0 ๐Ÿ”ฅ

Component
Baseย  โ†’

Last updated about 3 hours ago

Created by

๐Ÿ‡ซ๐Ÿ‡ทFrance fancyweb

Live updates comments and jobs are added and updated live.
  • Needs reroll

    The patch will have to be re-rolled with new suggestions/changes described in the comments in the issue.

Sign in to follow issues

Merge Requests

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • ๐Ÿ‡ฆ๐Ÿ‡บAustralia larowlan ๐Ÿ‡ฆ๐Ÿ‡บ๐Ÿ.au GMT+10
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia prem suthar Ahemdabad- Gujrat , Jodhpur - Rajsthan

    Prem Suthar โ†’ made their first commit to this issueโ€™s fork.

  • Status changed to Needs review about 1 year ago
  • ๐Ÿ‡ฎ๐Ÿ‡ณIndia prem suthar Ahemdabad- Gujrat , Jodhpur - Rajsthan

    Added the Mr For the #13 for re-roll the patch .

  • Pipeline finished with Failed
    about 1 year ago
    Total: 386s
    #99189
  • Pipeline finished with Failed
    about 1 year ago
    Total: 377s
    #99290
  • Status changed to Needs work about 1 year ago
  • The Needs Review Queue Bot โ†’ tested this issue. It fails the Drupal core commit checks. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide โ†’ to find step-by-step guides for working with issues.

  • ๐Ÿ‡ง๐Ÿ‡ชBelgium wim leers Ghent ๐Ÿ‡ง๐Ÿ‡ช๐Ÿ‡ช๐Ÿ‡บ

    I ran into this as well for https://wimleers.com after upgrading from Drupal 7 to 10. It made me pull my hair out for hours. Although I'm on a very atypical setup: local server at home, via non-standard port (ISP blocks port HTTP(S) ports) as HTTP, served by a CDN as HTTPS. At first, I just thought I was doing something wrong in my exotic (but dead simple!) setup, but it turns out to have been this long-standing Drupal core bug ๐Ÿ™ˆ

    Even though it's a single site! Because many years ago, it was considered a best practice to not use sites/default/files, but sites/example.com/files. Because I don't use sites/default, the same code path is triggered as for a multisite.

    I didn't really need trusted proxies; I only needed the reverse proxy (Fastly) to be trusted, to ensure that the TLS termination at the Fastly edge was respected.

    While I first hacked core (๐Ÿ™ˆ), I then settled on something simpler: running this early enough, aka before Drupal's index.php is executed:

    // When any non-local IP acccesses this, serve this as if it's the live site.
    // (It should be a Fastly IP address, and it is possible to validate against that, but this suffices.)
    // @see https://www.fastly.com/documentation/reference/api/utils/public-ip-list/
    if (filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
      $_SERVER['HTTP_HOST'] = 'wimleers.com';
      // Fastly also does TLS termination for us, so make Drupal think it's accessed via https://.
      // @see https://docs.fastly.com/en/guides/tls-termination#when-using-wordpress
      $_SERVER['HTTPS'] = 'on';
    }
    

    (Because doing it in settings.php is too late.)

  • ๐Ÿ‡ฎ๐Ÿ‡นItaly apaderno Brescia, ๐Ÿ‡ฎ๐Ÿ‡น
Production build 0.71.5 2024