Введение
В данной статье я расскажу про опыт настройки Nginx в качестве прокси-сервера для сервера с bitrixVM. При типовой настройке сервера с bitrix и последующего конфигурирования Nginx в качестве прокси для него, где, казалось бы, все параметры типовые и нужно ли лишь минимально настроить proxy_pass до нужного адреса, часто могут возникать ошибки, которые всплывают в процессе эксплуатации.
Проблема подстановки 80 или 443 порта в URL
Суть ошибки понятна из её содержимого. Такое бывает из-за того, что терминация TLS-трафика происходит на стороне прокси (Nginx), т.е. запросы пользователей приходят на 443 порт, а backend с сайтом на битрикс работает на http – обычно 80 или иной другой порт, а потому от прокси до бэкенда трафик идёт уже незашифрованный. Сам apache, который является конечной точкой в схеме при проксировании, должен понимать, что сайт работает с использованием протокола https, т.е. при выводе информации в phpinfo значение переменной $_SERVER[‘SERVER_PORT’] должно быть 443, а не 80, и в таком случае никакой некорректной подстановки осуществляться не будет.
- Самым простым решением является использовать 80 или 443 порт в зависимости от используемого протокола – реализация взята с сайта битрикс и настраивается через конструкцию map. Например, на сервере с битрикс создается файл /etc/nginx/bx/settings/schema.conf со следующим содержимым:
map $http_x_forwarded_proto $balancer_port {
default 80;
"https" 443;
}
map $http_x_forwarded_proto $balancer_https {
default "NO";
"https" "YES";
}
Если переменная $http_x_forwarded_proto в Nginx содержит в себе https, то в новые переменные $balancer_port и $balancer_https записывается значение 443 и YES соответственно.
А в используемом сайте по пути, например, /etc/nginx/bx/site_enabled/bx_ext_test.example.org.conf заменить конфигурацию по умолчанию:
proxy_set_header Host $host:80;
На следующую:
proxy_set_header Host $host:$balancer_port;
proxy_set_header HTTPS $balancer_https;
Таким образом, при работе сайта по https или http, всегда будет подставляться корректный порт в зависимости от используемого протокола.
- В своё время по этой проблеме я также писал в поддержку битрикса и тогда они ещё предложили такое костыльное решение, как мне кажется. Но оно имеет право на жизнь, т.к. работает. В dbconn.php надо добавить:
if (($pos = strpos($_SERVER['HTTP_HOST'], ':')) !== false)
{
$HTTP_HOST = $_SERVER['HTTP_HOST'] = substr($_SERVER['HTTP_HOST'],0,$pos);
}
$_SERVER["HTTPS"] = "On";
$_SERVER['SERVER_PORT'] = 443;
По сути будет выполнено тоже самое, что и описанное ранее выше, только средствами PHP, и Apache будет понимать, что сайт работает через https.
- И есть ещё один из самых правильных вариантов. Также можно внести правки на стороне nginx, который проксирует непосредственно на сам backend к PHP, т.е. в nginx на сервере с bitrix. Например, добавить в nginx.conf в секции HTTP:
http {
...
proxy_redirect ~^http://([^:]+):443(/.+)$ https://$1$2;
...
}
Директива proxy_redirect заменит любой запрос заголовка location, который матчится с регуляркой при наличии 443 порта, на корректную схему уже с https. Более подробно в документации Nginx.
400 Bad Request, The plain HTTP request was sent to HTTPS port
Не совсем понятная на первый взгляд ошибка. При перенаправлении с http на https на вышестоящем Nginx proxy и обращении к имени сайта https://domain.ru/bitrix без слэша в конце, в URL подставлялся 443 порт и протокол менялся на http.
Проблема ошибки 400 Bad Request, The plain HTTP request was sent to HTTPS port заключается в модуле mod_dir у httpd. При настройке редиректа на вышестоящим прокси-сервере и открытии адреса вида domain.ru/bitrix без закрывающего слеша в конце получается проблема, когда domain.ru/bitrix – это директория. А для директорий требуется закрывающий слеш в конце. Т.е. если в Nginx даже указать proxy_set_header HTTPS YES, то для данного URL с директорией это не сработает, а потому нужно явно указать в httpd, что сайт работает по https, т.е. прописать схему в конфиг нужного virtualhost.
- в конфигурационном файле httpd по пути /etc/httpd/bx/conf/bx_ext_test.example.org.conf явно указать в директиве ServerName имя домена и схему https. Если используется дефолтный конфиг без отдельных сайтов, то вписать в дефолтный конфиг /etc/httpd/bx/conf/default.conf:
ServerName https://test.example.org
Проксирование websockets для Push&Pull
Ещё одна известная ошибка, которая возникает при работе сайта на битриксе через прокси – это не работает модуль Push&Pull. Точнее, не работает проверка системы, хотя казалось бы, что всё настроено: из menu.sh bx-push-server 2.0 установлен корректно штатными средствами, redis запущен, в логах ошибок нет. Но на сайте может быть красная полоса “Отсутствует соединение с сервером” и проваливается проверка системы, если обращаться через прокси-сервер.
Проблема в том, что Push server работает через wss, т.е. вебсокеты, а для этого со стороны прокси сервера необходимо проксировать дополнительные заголовки до backend, указывая явно, что клиент может сменить протокол на wss. “Upgrade” и “Connection” не передаются от клиента к проксируемому серверу, поэтому, для того чтобы проксируемый сервер узнал о намерении клиента сменить протокол на WebSocket, эти заголовки следует передать явно.
Для реализации необходимо на прокси-сервере добавить отдельный location:
location ~* ^/bitrix/subws/ {
access_log off;
proxy_pass http://BACKEND;
proxy_max_temp_file_size 0;
proxy_read_timeout 43800;
proxy_http_version 1.1;
proxy_set_header Upgrade $replace_upgrade;
proxy_set_header Connection $connection_upgrade;
А в http секции должна быть конструкция из map, определяющая значение вышеописанных переменных:
map $http_upgrade $connection_upgrade {
default upgrade;
'' 'close';
}
map $http_upgrade $replace_upgrade {
default $http_upgrade;
'' "websocket";
}
Настройка basic auth
При настройке http-авторизации на прокси-сервере с Nginx может случиться проблема с авторизацией на сайте с битрикс.
Пример следующий: на стороне Nginx basic auth успешно проходит после ввода верного логина\пароля, и пускает пользователя дальше на проксируемый ресурс. Но битрикс почему-то принимает логин и пароль от basic auth в свою веб-форму авторизации и выдает ошибку “неверный логин или пароль”. Данная проблема по началу может смутить, т.к. на первый взгляд нет никакой связи между файлом для basic auth, созданным через htpasswd, и веб-формы авторизации сайта.
Но объяснение этого достаточно просто: при использовании модуля ngx_http_auth_basic_module формируется HTTP-заголовок “Authorization”, где зашиваются зашифрованные в base64 логин и пароль, которые пользователь вводит при появлении окна basic auth. А после того, как логин\пароль введён верно, запрос пользователя проксируется на сайт с битрикс, а вместе с ним и заголовок “Authorization”. И тут в игру вступает битрикс и веб-сервер apache – по умолчанию в файле .htaccess сайта, созданного через bitrix-env, есть следующая строка:
<IfModule mod_rewrite.c>
Options +FollowSymLinks
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-l
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$
RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L]
RewriteRule .* - [E=REMOTE_USER:%{HTTP:Authorization}]
</IfModule>
В переменную REMOTE_USER добавляется значение из HTTP-заголовка HTTP:Authorization, в котором содержится логин\пароль для basic auth. А дальше битрикс пробует авторизовать пользователя уже в своей форме на основе REMOTE_USER, делается это через код.
На сайте битры об этом написано и там даже предлагаются различные решения – правки для кода. Но вся проблема в том, что это нарушение исходной целостности кодовой базы и проблема решается куда проще двумя иными способами:
- В .htaccess можно просто закомментировать строку RewriteRule .* – [E=REMOTE_USER:%{HTTP:Authorization}]. Это допустимо при условии, что в дальнейшем не потребуется SSO через Kerberos или NTLM, т.к. как раз на основе переменной REMOTE_USER битрикс и производит сквозную авторизацию.
- И самое правильное решение, разумеется, находится на стороне Nginx. Для заголовка HTTP:Authorization просто подставляется пустое значение, а потому битриксу будет неоткуда взять значение для REMOTE_USER, и сайт попросит ввести логин\пароль уже в стандартную форму.
location / {
proxy_set_header Authorization "";
...
}
Итоговый пример конфигурационного файла
Ниже приведен пример конфигурационного файла nginx proxy для сервера с битрикс:
server {
server_name {DOMAIN};
listen 80;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name {DOMAIN};
ssl_certificate "/etc/nginx/ssl/cert.crt";
ssl_certificate_key "/etc/nginx/ssl/key.key";
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/{DOMAIN}.access.log;
location / {
proxy_ignore_client_abort on;
proxy_pass http://{NODE-IP}:80;
proxy_redirect http://{NODE-IP}:80 /;
proxy_read_timeout 300;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header HTTPS YES;
proxy_set_header Authorization "";
# for Push&Pull
location /bitrix/subws {
proxy_pass http://{NODE-IP}:80;
proxy_set_header Upgrade $replace_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect http://{NODE-IP}:80 /;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header HTTPS YES;
}
}
Заключение
В статье рассмотрел основные проблемы, которые приходилось встречать и решать самостоятельно. В рунете по той или иной проблеме есть много советов, но всё хаотично разбросано и не всегда есть объяснения, как работает и как решить тот или иной вопрос. Поэтому если сделать всё по моей статье в комплексе, то ошибок возникать не должно.
Используемые источники
- https://dev.1c-bitrix.ru/learning/course/index.php?COURSE_ID=32&LESSON_ID=13682&LESSON_PATH=3903.4862.8809.8877.13678.13682
- https://nginx.org/ru/docs/http/websocket.html
- https://stackoverflow.com/questions/35660830/issue-with-change-protocol-after-301-redirect-nginx-bitrixvm-centos6-5
- http://httpd.apache.org/docs/2.2/mod/core.html#servername
Очень полезная статья. Да и оформление грамотное, не только этой но и других статей. Приятно и леко читать. Бегпал пофорумам, искал решение, ваша статья очень помогла) Спасибо за помощь!
И вам спасибо! Рад, что материал пригодился – для этого и было всё написано