Я развернул приложение Yii2
на основе PHP
на OpenShift
. По сути, Yii2
работает в контейнере на основе alpine
с установленными PHP
и Nginx
. Контейнер предоставляет port 8080
, который затем привязывается к сервису OpenShift
, где, в свою очередь, создается защищенный маршрут с Edge encryption
.
Большая часть приложения работает нормально, однако при создании сообщения HTTP
, а затем в контроллере, выполняющем перенаправление, Yii2
перенаправляет на http://<sitename>
вместо того, чтобы оставаться на https
, что приводит к тому, что приложение больше не работает. Сам запрос POST
работает, так как впоследствии в БД вносятся изменения, это перенаправление обратно к обзору, которое не работает в этом контексте.
Действие контроллера очень простое и стандартное:
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
}
return $this->render('update', [
'model' => $model,
]);
}
Немного информации о конфигурации OpenShift:
~ oc get routes
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
backend backend.apps.<instance> backend backend edge None
frontend frontend.apps.<instance> frontend frontend edge None
~ oc describe route backend
Name: backend
Namespace: <project>
Created: 10 hours ago
Annotations: openshift.io/host.generated=true
Requested Host: backend.apps.<instance>
exposed on router router 10 hours ago
Path: <none>
TLS Termination: edge
Insecure Policy: <none>
Endpoint Port: backend
Service: backend
Weight: 100 (100%)
Endpoints: <ip>:8080
Dockerfile для создания приложения не очень впечатляющий, он в основном устанавливает PHP
, Nginx
и устанавливает некоторые разрешения. Конфигурация Nginx
на самом деле может быть частью проблемы, поэтому мы публикуем очищенную версию ниже.
worker_processes 1;
error_log stderr warn;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main_timed '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time $pipe $upstream_cache_status';
access_log /dev/stdout main_timed;
error_log /dev/stderr notice;
keepalive_timeout 65;
server {
listen [::]:8080 default_server;
listen 8080 default_server;
server_name _;
sendfile off;
root /var/www/html/backend/web/;
index index.php index.html;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to index.php
try_files $uri $uri/ /index.php?q=$uri&$args;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/lib/nginx/html;
}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
location ~* \.(jpg|jpeg|gif|png|css|js|ico|xml)$ {
expires 5d;
}
# deny access to . files, for security
#
location ~ /\. {
log_not_found off;
deny all;
}
# allow fpm ping and status from localhost
#
location ~ ^/(fpm-status|fpm-ping)$ {
access_log off;
allow 127.0.0.1;
deny all;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
}
}
}
Редактировать 1:
Добавление следующей строки в конфигурацию Nginx каким-то образом заставляет его работать, однако это не очень хорошее решение:
location ~ \.php$ {
[...]
fastcgi_param HTTPS on;
Однако это предполагает, что всегда используется HTTP. Однако это зависит от конфигурации маршрута OpenShift. Есть ли лучшее (более универсальное) решение?
@GrahamDumpleton спасибо за ваш комментарий! Мой главный вопрос заключается в том, где устанавливается заголовок (возможно, с помощью прокси-сервера OpenShift/lb)? Как указать Nginx пересылать этот заголовок с помощью fastcgi?
@waza-ari, вы можете попробовать следующее fastcgi_pass_header X-Proxy-Scheme;
nginx.org/ru/docs/http/… или fastcgi_pass_request_headers on;
nginx.org/ru/docs/http/…
@Александр, если я правильно понял руководство, fastcgi_pass_request_headers
включен по умолчанию.
Маршрутизатор OpenShift (который предоставляет защищенный маршрут, который вы создали) установит число заголовки в запросе к бэкэнд-модулю, на котором запущено ваше приложение Yii2.
Первое, что нам нужно сделать, — это доверить эти заголовки Nginx, чтобы он передал их серверному приложению Yii2.
location ~ \.php$ {
proxy_pass_header X-Forwarded-Proto;
proxy_pass_header X-Forwarded-Host;
proxy_pass_header X-Forwarded-Port;
[...]
X-Forwarded-Proto
будет установлено на https
, и похоже, что Yii2 будет правильно обрабатывать заголовок, но я раньше не использовал эту PHP-инфраструктуру. Это будет второй шаг, если потребуется, который будет заключаться в том, чтобы получить URL-адрес ответа фреймворка с протоколом, который находится в заголовке x-forwarded-proto.
Здесь следует отметить, что доверять этим заголовкам на уровне Nginx может быть опасно, если ненадежные пользователи могут получить прямой доступ к Nginx. В этом сценарии они смогут подделывать запросы и устанавливать эти заголовки такими, какие они хотят.
Благодаря всем комментариям и ответам, я смог понять это. Я не использую Nginx в качестве прокси здесь, но я использую FastCGI для PHP, поэтому я не уверен, что ответ, предложенный Джейсоном, сработает. Следующие решения работают для меня:
location ~ \.php$ {
set $my_https 'off';
if ($http_x_forwarded_proto = 'https') {
set $my_https 'on';
}
fastcgi_param HTTPS $my_https;
Может быть более элегантное решение для этого, но это работает для меня.
Ваше приложение должно учитывать заголовок
X-Proxy-Scheme
, установленныйhaproxy
, который используется для маршрутизации. Значение скажет вам, использовал ли исходный запросhttp
илиhttps
. Вы должны использовать это в любых URL-адресах, которые создает ваше приложение.