Как фильтровать запросы в nginx по IP, Referer или User-Agent

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

У меня проблема с фильтрацией запросов на основе таких параметров, как IP, Referer и/или User-Agent.
Что я хотел бы достичь, так это разрешить доступ к определенному URL на основе заголовка IP или Referer.

Что я пробовал, это в моем default.conf на основе этого https://gist.github.com/bdmorin/e2a8574192718f03b58f и некоторых других темах здесь о вложенных if я создал следующее:

geo $allowedipaddr {
    default             1;
    10.10.17.10         0;
}

server {
(...)

    location @proxy {
        proxy_pass http://internalserver/
        (...)
    }

    location ~* ^/rest(/([^/]+))?/V1/ {
        valid_referers server_name domain.com/check;

        set $referer "";
        set $allowip "";
        
        if ($invalid_referer) {
            set $referer "N";
        }

        if ($allowedipaddr) {
            set $allowip "N";
        }

        set $test "${allowip}${referer}";
        if ($test = NN) {
            return 403;
        }
        
        try_files "" @proxy;
    }

После тестов это работает только тогда, когда заголовок referer присутствовал в запросе, оно не разрешало доступ иным способом, даже когда IP был в списке разрешенных. Когда referer был действительным, а IP нет – запрос не проходил. Когда referer был недействительным или отсутствующим, а IP был в порядке – запрос не проходил.
Я думал, что все if внутри location проверяются, но, как я правильно понимаю из других тем и документации, nginx находит первое ‘совпадение’ и прекращает проверять остальное.

Таким образом, я изменил подход и создал (как оказалось позже) то же самое решение, но другим способом:

location ~* ^/rest(/([^/]+))?/V1/(categories|guest-carts|integration|inventory|orders|products|products-render-info|stockItems|store|taxRates|taxRules)(/|$) {

    if ($allowedipaddr){
        return 403;
    }

    try_files "" @proxy;
}


location ~* ^/rest(/([^/]+))?/V1/ {
    valid_referers server_name domain.com/check;

    if ($invalid_referer) {
        return 403;
    }

    try_files "" @proxy;
}

Я хотел создать большую область, где разрешены только запросы с referer, и меньшую область этой большой области, где разрешены IP-адреса. Но, как и ранее, nginx находит первое ‘совпадение’ и не проверяет остальное.
Я думал разместить location внутри location, но думаю, что это будет работать так же, но в меньше линий кода.

Так как я могу разрешить доступ к определенному URL, если заголовок Referer или IP допустимы?

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

Чтобы фильтровать запросы в NGINX на основе IP-адреса, Referer или User-Agent, необходимо учитывать особенности работы директив и порядок их применения. Ваша задача заключается в том, чтобы предоставить доступ к определённому URL, если Referer или IP-адрес являются допустимыми. Рассмотрим, как это можно реализовать с учётом предоставленных данных.

Определение IP-адресов

Для фильтрации по IP-адресам вы уже используете блок geo, который правильно определяет IP-адреса, которым разрешён доступ. Но необходимым дополнением является корректное использование этой информации далее в конфигурации.

geo $allowedipaddr {
    default 1;
    10.10.17.10 0;
}

Написание конфигурации сервера

Существуют два основных подхода, с помощью которых можно управлять доступом на уровне location.

Конфигурация на уровне location

server {
    location ~* ^/rest(/([^/]+))?/V1/ {
        # Проверяем валидность Referer
        valid_referers none blocked server_name domain.com/check;

        set $access_granted 0; # Изначально доступ заблокирован

        if ($allowedipaddr = 0) {
            set $access_granted 1;
        }

        if ($http_referer ~* "(domain.com/check|server_name)") {
            set $access_granted 1;
        }

        if ($access_granted = 0) {
            return 403; # Отказ в доступе
        }

        # Осуществляем проксирование запроса
        proxy_pass http://internalserver/;
    }
}

Конфигурация через map (более гибкий способ)

Использование директивы map предлагает более гибкий способ определения доступа.

map $http_referer $is_valid_referer {
    "~^(https?://)?(www\.)?domain\.com/check$" 1;
    default 0;
}

map $allowedipaddr $is_valid_ip {
    0 1;
    default 0;
}

server {
    location ~* ^/rest(/([^/]+))?/V1/ {
        set $access_granted 0; # Изначально доступ заблокирован

        if ($is_valid_ip = 1) {
            set $access_granted 1;
        }

        if ($is_valid_referer = 1) {
            set $access_granted 1;
        }

        if ($access_granted = 0) {
            return 403; # Отказ в доступе
        }

        proxy_pass http://internalserver/;
    }
}

Объяснение

  • geo и map Директивы: С помощью этих директив мы определяем переменные, которые используются в условных конструкциях для фильтрации IP и Referer. geo помогает определить наличие IP в дозволенном списке, тогда как map может фильтровать значения заголовков более гибко.

  • valid_referers: Эта директива используется для проверки Referrer. Она определяет, какие значения заголовка Referer считаются допустимыми.

Заключение

Правильная реализация условных конструкций (if), а также использование переменных позволяют управлять доступом более точно. Неправильный порядок выполнения или неверное использование могут приводить к тому, что NGINX пропускает проверки. Убедитесь, что установлены ожидаемые и корректные значения переменных, чтобы избежать нежелательной блокировки или разрешения.

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

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