Расширение существующей конфигурации местоположения вместо её перезаписи

Вопрос или проблема

Что у меня есть

У меня настроен Nginx для обработки файлов .php для всех виртуальных хостов, и все работает хорошо.

location ~ \.php {
    include snippets/fastcgi-php.conf;
    keepalive_timeout 0;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
}

Вышеизложенное находится в snippets/common.conf и включается в каждую конфигурацию виртуального хоста. Упомянутый fastcgi-php.conf находится рядом с ним.

Что мне нужно

Для данного виртуального хоста мне нужно запретить доступ ко всем файлам, кроме /example.php.

Что я пробовал

  1. location ~ ^/example {
        allow all;
    }
    location ~ ^/ {
        allow 123.123.123.123;
        deny all;
    }
    

    Само ограничение работает хорошо, но PHP файл теперь возвращается как текст исходного кода, вместо обработки. Это означает, что вышеупомянутое каким-то образом перезаписывает предыдущий location ~ \.php, даже если я здесь не включал расширение .php.

  2. Создать common-php.conf

    include snippets/fastcgi-php.conf;
    keepalive_timeout 0;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_pass unix:/run/php/php7.4-fpm.sock;
    

    Повторно использовать его в common.conf:

    location ~ \.php {
        include snippets/common-php.conf;
    }
    

    И в моей фактической конфигурации виртуального хоста:

    location ~ ^/example {
        allow all;
        include snippets/common-php.conf;
    }
    location ~ ^/ {
        allow 123.123.123.123;
        deny all;
        include snippets/common-php.conf;
    }
    

    Проблемы с вышеизложенным:

    • Это значительно усложняет настройку
    • Казалось бы избыточным настраивать /example.php дважды
    • Это не полностью работает, так как я получаю 403, например, на /plugins/Feedback/angularjs/feedback-popup/feedback-popup.directive.html?cb=abcd1234 по какой-то причине. CMS, которую я использую, сложна, и по предыдущим двум причинам я не проводил дальнейшее расследование.

Я хотел бы настроить доступ к файлу /example.php, не перезаписывая существующий обработчик PHP, прикрепленный к нему. Это возможно? Или включения — это мой единственный вариант?

Пожалуйста, дайте мне знать, если вы можете придумать лучшую конфигурацию для этой задачи. Спасибо!

Обновление @ 2025.02.26

Исходная версия этого ответа (можно просмотреть через историю редактирования) не работала по причине того, что директива geo не позволяет использовать переменные для значения производной переменной. При цепочке блоков map и geo блок geo всегда должен быть последним звеном в цепи.


То есть, очень распространенная ошибка заключается в следующем

location / {
    ... некоторые настройки
}
location /admin {
    ... дополнительные настройки
}

и ожидании, что запрос /admin/... использует комбинированные настройки. Этого не произойдет, в зависимости от местоположения, где запрос будет в конечном итоге обработан, будет применен только один набор настроек. Однако, как я уже говорил, каждый запрос может проходить через несколько блоков местоположения, и некоторые трюки можно выполнить, используя манипуляции с переменными. Определенной переменной можно изменить значение несколько раз, пока запрос проходит через разные местоположения, и ее фактическое значение можно использовать в конечном местоположении в качестве параметра директивы. Решение данного вопроса не будет использовать эту технику, но это возможно в некоторых других случаях.

Многие вещи (например, условные заголовки) могут быть реализованы с использованием (очень мощной) функции map в nginx. Альтернатива правилам allow/deny может быть достигнута, комбинируя блоки map и geo, чтобы оценить переменную, действующую как флаг, который указывает, должен ли запрос быть заблокирован или нет (некоторый более общий пример можно найти здесь):

geo $restricted {
    123.123.123.123  0;
    default          1;
}

map $uri $deny {
    /example.php     0;
    default          $restricted;
}

Теперь, чтобы принять решение, должен ли запрос быть заблокирован, вы можете использовать следующий блок if:

if ($deny) { return 403; }

Этот блок может быть размещен либо на уровне server, либо на уровне обработчика PHP. Однако размещение его на уровне server будет более оптимальным, так как оно будет выполнено на более раннем этапе NGX_HTTP_SERVER_REWRITE_PHASE, вместо NGX_HTTP_REWRITE_PHASE.

Если по какой-то причине вы захотите добавить базовую аутентификацию вместо фильтрации по IP, это можно сделать, используя следующий трюк:

geo $restricted {
    123.123.123.123  off;
    default          "Protected area";
}

map $uri $realm {
    /example.php     off;
    default          $restricted;
}
location ~ \.php$ {
    auth_basic $realm;
    auth_basic_user_file /path/to/.htpasswd;
    ...
}

.

Ответ или решение

Теория

При конфигурировании Nginx для управления различными специфическими запросами к вашему серверу, важно понимать, как Nginx обрабатывает директивы location. В Nginx, порядок очередности и специфичность правил играет ключевую роль. Запросы сопоставляются с location в порядке строгой иерархии, начиная с более специфичных и заканчивая более общими совпадениями.

Например, location с регулярным выражением (location ~ \.php$) будет обрабатывать все файлы с расширением .php. Если есть специфичные требования для пары запросов, для которых нужен специальный доступ, можно использовать более конкретные совпадения, такие как location = /example.php. Важно также помнить, что внутри одного location блока можно управлять процессом с использованием переменных, которые определяют, как должны обработаться запросы.

Пример

Предположим, требуется настроить сервер так, чтобы доступ был разрешен только к одному конкретному файлу example.php, но при этом остались активными все остальные существующие настройки для других PHP-файлов. Описанная вами проблема заключается в том, что более общие настройки location ~ \.php$ перезаписываются, когда вы добавляете новые более специфичные location блоки, что приводит к тому, что PHP-код возвращается в открытом виде (в виде текста), вместо того чтобы обрабатываться FastCGI как должно.

Применение

Ваша задача может быть решена посредством использования более специфичных директив location, а также map и geo блоков для динамического управления доступом.

  1. Использование более специфичного location блока:
    Для определения специфичной логики обработки example.php, вам стоит использовать точное совпадение:

    location = /example.php {
       include snippets/fastcgi-php.conf;
       fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
       fastcgi_pass unix:/run/php/php7.4-fpm.sock;
       allow all;
    } 

    Это гарантирует, что example.php будет обрабатываться как PHP скрипт и доступ будет открыт для всех.

  2. Управление доступом через IP-адреса:
    Если необходимо ограничение на доступ к другим файлам через IP-адреса, можно использовать geo и map для более гибкого управления:

    geo $restricted {
       123.123.123.123  0;
       default          1;
    }
    
    map $uri $deny {
       /example.php     0;
       default          $restricted;
    }
    
    # использование if на серверном уровне
    server {
       # другие серверные конфигурации
       if ($deny) {
           return 403;
       }
    }

    В этом примере мы заранее определяем, к каким файлам может быть открыт доступ, а к каким доступ будет ограничен, исходя из IP-адреса.

  3. Базовая аутентификация:
    Если нужно реализовать базовую аутентификацию на основе URI, можно реализовать это следующим образом:

    geo $restricted {
       123.123.123.123  off;
       default          "Protected Area";
    }
    
    map $uri $realm {
       /example.php     off;
       default          $restricted;
    }
    
    location ~ \.php$ {
       auth_basic $realm;
       auth_basic_user_file /path/to/.htpasswd;
    }

Этот метод позволяет установить уникальные параметры аутентификации для различных файловых путей.

Заключение

Таким образом, используя комбинацию точных location директив и механизмов geo и map, можно добиться более гибкого и надежного решения задачи, избегая избыточности и нежелательного перекрытия настроек. Для гибкой настройки доступа это подход обеспечит полноценную конфигурацию и помощи в соблюдении всех требований безопасности и производительности.

Оцените материал
Добавить комментарий

Капча загружается...