Как сконфигурировать Symfony, чтобы она работала за распределителем нагрузки или обратным прокси

Дата обновления перевода 2024-07-19

Как сконфигурировать Symfony, чтобы она работала за распределителем нагрузки или обратным прокси

Когда вы разворачиваете своё приложение, вы можете находиться за распределителем нагрузки (например, эластичное распределение нагрузки AWS) или обратным прокси (например, Varnish для кеширования).

В большинстве случаев это не вызывает проблем с Symfony. Но, когда запрос проходит через прокси, отправляется определённая информация запроса, используя либо стандартный заголовок Forwarded, либо заголовки X-Forwarded-*. Например, вместо прочтения заголовка REMOTE_ADDR (который теперь будет IP адресом вашего обратного прокси), настоящий IP пользователя будет храниться в стандартном заголовке Forwarded: for="..." или заголовке X-Forwarded-For.

Если вы не сконфигурируете Symfony так, чтобы она искала такие заголовки, то вы будете получать неправильную информауию об IP адресе клиента, независимо от того, подключается он через HTTPS или нет, клиентского порта и запрашиваемого имени хоста.

Решение: setTrustedProxies()

Чтобы исправить это, вам нужно сказать Symfony, каким IP адресам обратного прокси можно доверять, и какие заголовки использует ващ прокси для отправки информации:

1
2
3
4
5
6
7
8
9
10
11
# config/packages/framework.yaml
framework:
    # ...
    # IP-адрес (или диапазон) вашего прокси
    trusted_proxies: '192.0.0.1,10.0.0.0/8'
    # сокращение для диапазона приватных IP-адресов вашего прокси
    trusted_proxies: 'private_ranges'
    # доверять *всем* заголовкам "X-Forwarded-*"
    trusted_headers: ['x-forwarded-for', 'x-forwarded-host', 'x-forwarded-proto', 'x-forwarded-port', 'x-forwarded-prefix']
    # или, если ваш прокси вместо этого использует заголовок "Forwarded"
    trusted_headers: ['forwarded']

7.1

private_ranges в качестве сокращения для диапазона приватных IP-адресов для опции trusted_proxies было представлено в Symfony 7.1.

Caution

Включение опции Request::HEADER_X_FORWARDED_HOST делает приложение уязвимым к атакам заголовков хостинга HTTP. Make sure the proxy really sends an x-forwarded-host header.

Объект Запрос имеет несколько констант Request::HEADER_*, которые контролирует, каким заголовкам вашего обратного прокси можно доверять. Аргумент яляется полем бита, так что вы также можете передать собственное значение (например, 0b00110).

Tip

Вы можете установить переменную окружения TRUSTED_PROXIES, чтобы конфигурировать прокси, основываясь на окружении:

1
2
# .env
TRUSTED_PROXIES=127.0.0.1,10.0.0.0/8
1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    trusted_proxies: '%env(TRUSTED_PROXIES)%'

Danger

Функция "доверенные прокси" не работает, как ожидается, при использовании модуля nginx realip. Отключите этот модуль при обслуживании приложений Symfony.

Но что, если IP моего обратного прокси постоянно меняется!

Некоторые обратные прокси (вроде Эластичного распределения нагрузки AWS) не имеют статичного IP адреса или даже диапазона, который вы можете охватить с помощью CIDS примечания. В этомслучае, вам нужно будет - очень осторожно - доверить всем прокси.

  1. Сконфигурируйте ваш(и) веб-сервер(ы), чтобы они не отвечали на траффик любых клиентов, кроме ваших распределителей нагрузки. Для AWS это можно сделать с помощью групп безопасности.
  2. Когда вы гарантировали, что траффик будет исходить только от доверенных обратных прокси, сконфигурируйте Symfony всегда доверять входящему запросу:

    1
    2
    3
    4
    5
    6
    # config/packages/framework.yaml
    framework:
        # ...
        # доверять *всем* запросам (строка 'REMOTE_ADDR' заменяется во время
        # прогона на $_SERVER['REMOTE_ADDR'])
        trusted_proxies: '127.0.0.1,REMOTE_ADDR'

Вот и всё! Критично важно, чтобы вы предотвратили траффик из всех недоверенных источников. Если вы позволите сторонний траффик, он может поддежать настоящие IP адреса и другую информацию.

Если вы также используете обратный прокси поверх вашего балансировщика нагрузки (например, CloudFront), вызова $request->server->get('REMOTE_ADDR') будет недостаточно, так как он будет доверять только узлу, находящемуся прямо над вашим приложением (в данном случае - вашему балансировщику нагрузки). Вам также нужно добавить IP-адреса или диапазона любой дополнительный прокси (например, IP-диапазоны CloudFront) к массиву доверенных прокси.

Обратный прокси в подпути / подпапке

Если ваше приложение Symfony работает за обратным прокси и обслуживается в подпути/подпапке, Symfony может генерировать некорректные URL, которые игнорируют подпуть/подпапку обратного прокси.

Чтобы решить эту проблему, вам нужно передать префикс маршрута подпути/подпапки обратному прокси в Symfony, установив заголовок X-Forwarded-Prefix. Этот заголовок
обычно настраивается в конфигурации вашего обратного прокси. Сконфигурируйте X-Forwarded-Prefix как доверенный заголовок, чтобы иметь возможность использовать
эту функцию.

Префикс X-Forwarded-Prefix используется Symfony для добавления префикса к базовому URL-адресу объектов запроса, который используется для генерирования абсолютных путей и URL-адресов. Без этого заголовка базовый URL будет определяться только на основе конфигурации веб-сервера, на котором работает Symfony, что приводит к неправильным путям/URL, когда приложение обслуживается в подпути/подпапке обратным прокси.

Например, если ваше приложение Symfony напрямую обслуживается по такому URL, как
https://symfony.tld/, а вы хотите использовать обратный прокси для обслуживания приложения по адресу https://public.tld/app/, вам нужно установить заголовок X-Forwarded-Prefix в /app/ в конфигурации обратного прокси. Без этого заголовка Symfony будет генерировать URL на основе базового URL своего сервера (например, /my/route) вместо правильного /app/my/route, который требуется для доступа к маршруту через обратный прокси.

Заголовок может быть разным для каждого обратного прокси, так что доступ через разные обратные прокси, обслуживаемые в разных подпутях/подпапках, может быть обработан корректно.

Пользовательские заголовки при использовании обратного прокси

Некоторые обратные прокси (вроде CloudFront с CloudFront-Forwarded-Proto) могут заставлять вас использовать пользовательский заголовок. Например, у вас есть Custom-Forwarded-Proto вместо X-Forwarded-Proto.

В таком случае, вам понадобится установить заголовок X-Forwarded-Proto со значением Custom-Forwarded-Proto достаточно рано в вашем приложении, т.е. перед обработкой запроса:

1
2
3
4
5
6
// public/index.php

// ...
$_SERVER['HTTP_X_FORWARDED_PROTO'] = $_SERVER['HTTP_CUSTOM_FORWARDED_PROTO'];
// ...
$response = $kernel->handle($request);

Переопределение конфигурации за скрытым завершением SSL

Некоторые облачные системы (например, запуск контейнера Docker с помощью «Web App for Containers» в Microsoft Azure) выполняют завершение SSL и обращаются к вашему веб-серверу по HTTP, но не изменяют удаленный адрес и не устанавливают заголовки X-Forwarded-*. Это означает, что функция доверенного прокси в Symfony не сможет вам помочь.

После того как вы убедились, что ваш сервер доступен только через облачный прокси по HTTPS, а не через HTTP, вы можете переопределить информацию, которую ваш веб-сервер отправляет PHP. Для Nginx это может выглядеть следующим образом:

1
2
3
4
5
6
7
location ~ ^/index\.php$ {
    fastcgi_pass 127.0.0.1:9000;
    include fastcgi.conf;
    # Lie to Symfony about the protocol and port so that it generates the correct HTTPS URLs
    fastcgi_param SERVER_PORT "443";
    fastcgi_param HTTPS "on";
}