Маленький пример на NginX+GeoIP

Задача следующая. Есть фирма, в ней используется корпоративный антивирус Eset NOD32. Обновление происходит по протоколу HTTP с собственного зеркала, расположенного на веб-сервере в датацентре. Фирма состоит из крупных филиалов, мелких филиалов и сотрудников, работающих удалённо. Требуется достичь следующего поведения зеркала обновлений.

  • Если IP клиента соответствует мелкому филиалу (фиксирован и заранее известен), то отдать обновления без запроса логина-пароля.
  • Если IP клиента соответствует крупному филиалу (также фиксирован и заранее известен), то выдать HTTP 302 и перенаправить на другое зеркало, расположенное в локальной сети этого филиала с целью экономии трафика.
  • Во всех остальных случаях запросить логин и пароль. Если они окажутся верными, то отдать обновления.

В NginX подобные задачи рекомендуется решать директивой “satisfy any;”, но в данном случае такой трюк не проходит из-за необходимости делать редиректы. Приходится прибегать к помощи http_geo_module.

Работает он достаточно просто. В контексте http прописываем список IP адресов и вместе с ними значения переменной, которые будут присвоены в случае совпадения IP клиента со строкой в списке. По умолчанию переменная принимает значение, указанное в строке “default”. В моем случае:

    geo $geo {
         default                world;
         192.168.0.0/22         internal;
         1.2.3.4/32             internal;
         192.168.14.0/24        branch;
         5.6.7.8/32             branch;
        }

Где “$geo” — имя переменной; “world” — значение этой переменной, присваиваемое когда клиент пришел “из мира”, то есть не из локальных сетей; “internal” — “свои клиенты” (отдавать без пароля); “branch” — филиал с собственным зеркалом, то есть случай когда нужно делать редирект. Разумеется, все эти имена я придумал сам, они могут быть любыми.

Далее, в настройках сервера (или виртуального хоста) задаем примерно следующую конфигурацию.

server {
        listen   80;

        server_name  nod.my-company.ru nod;
        access_log  /var/log/nginx/nod.access.log;

        charset UTF-8;

        root /var/www/nod;
        autoindex  on;

        # Из "мира" только с авторизацией
        location @world {
         auth_basic "Please provide password to access updates";
         auth_basic_user_file nodpwd;
        }

        # Изнутри используем дефолтные настройки
        location @internal { }

        # Из филиала - перенаправление на внутренний сервер
        location @branch {
         rewrite ^ http://my.server.local/?;
        }

        location / {
        # Редирект в нужный именованный location
          error_page 418 = @$geo;
          return 418;
        }
}

В корневом location-е (последний в списке) анализируется переменная “$geo” и в зависимости от её значения происходит внутреннее перенаправление в один из именованных location-ов. А дальше уже можно творить всё что нам хочется, при этом поведение сервера будет определяться IP-адресом клиента.

Почему в предпоследнем location-е перенаправление идет в корень локального сервера, а не на “$request_uri”, объяснил в предыдущей заметке.