Nginx — уходим на технические работы

в 14:48, , рубрики: geo, maintenance, nginx, web, системное администрирование, технические работы, метки: , , , ,

image

Совсем недавно возникла интересная задача: реализовать закрытие доступа к веб-сайту из вне, на время технических работ. Мне показалось, что это довольно распространенная задачка, решение которой заинтересует многих.
Один из возможных вариантов решения — ниже.

Дано

  • Сервер: Ubuntu 10.04 LTS
  • Nginx в качестве фронт-энда

Задача

Закрыть доступ к веб-сайту со всех внешних IP-адресов, за исключением наших собственных (или любых других, при желании). При этом крайне желательно соблюдение следующих условий:

  • Время на «закрытие» должно быть минимальным (в иделе — меньше секунды)
  • Каждый раз менять конфиг Nginx при проведении работ — нельзя
  • Перезагружать Nginx при проведении работ тоже нельзя (ни restart, ни reload)

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

Решение

1. Собственно «Закрытие»

Было решено в качестве переключателя использовать триггер-файл (например, /etc/nginx/maintenance.file ). При его появлении Nginx должен будет возвращать код ошибки 503 и отображать соответствующую страничку. Для этого сохраняем куда-нибудь существующий конфиг:

cp /etc/nginx/sites-enabled/default /etc/nginx/sites-enabled/default.save

А потом вносим следующие изменения (выделены жирным шрифтом):
server {
 listen 80;
 server_name example.com;
 ...
 location / {
 if (-f /etc/nginx/maintenance.file) {
  return 503;
}
 # дальнейший конфиг остается неизменным
 ...
 ...
 }
 ...
 ...
 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
  root /etc/nginx/error; # папка со страничками ошибок
 }
}

Теперь, если веб-сервер обнаружит файл /etc/nginx/maintenance.file, то тут же выдаст 503-ю и отобразит страницу ошибки. Таким образом «закрытие/открытие» может быть осуществлено созданием/удалением триггер-файла соответственно.
В данном случае 503-я будет показана всем без разбора, что противоречит изначальным условиям. Как обойти это — далее.

2. Кому что показывать

Дабы иметь возможность визуального контроля содержимого веб-сайта при проведении различных манипуляций крайне желательно чтобы страничка «Ведутся технические работы» не показывалась нам самим. Для этого используем модуль ngx_http_geo_module. Как написано в документации: "… он создаёт переменные, значения которых зависят от IP-адреса клиента." Именно то, что нужно.

Мы хотим чтоб 503-я показывалась только если: одновременно и IP — внешний, и триггер-файл существует. Простейшее решение, которое приходит в голову — двойное условие или же два вложенных IF-а. К сожалению, ни первое ни второе Nginx не понимает. Поэтому придется делать «финт ушами». Еще немного модифицируем конфиг.
geo $maintenance
{
default yes;  #по умолчанию - всем закрыть доступ
 #далее указываем список IP, которым видеть страницу 503 не нужно
127.0.0.1/32 no;
123.45.67.0/24 no;
 ...
}
server {
 listen 80;
 server_name example.com;
 ...
 location / {
 if (-f /etc/nginx/maintenance.file) {
   set $tmp clo;
 }
 if ($maintenance = yes) {
  set $action "${tmp}se";
 }
 if ($action = close) {
  return 503;
 }

 # дальше опять конфиг остается неизменным
 ...
 ...
 }
 ...
 ...
 error_page 500 502 503 504 /50x.html;
 location = /50x.html {
  root /etc/nginx/error; # папка со страничками ошибок
 }
}

В секции location / указано подряд три условия, которые нам заменяют двойное условие: внешний IP плюс наличие триггер-файла.Тут следует немного объяснить как это работает. Проще всего это сделать на примере.
Пусть клиент с произвольным внешним IP запрашивает страницу. Кроме того: файл /etc/nginx/maintenance.file уже существует.

  1. Так как триггер-файл создан: $tmp = "clo"
  2. Айпишник не из списка исключений: $maintenance = "yes"
  3. Поскольку $maintenance = "yes", то: $action = $tmp + "se" = "close"
  4. $action = "close", значит происходит return 503

Все «чужие» видят страницу 503, а для нашей сети одно из условий не выполняется и переменная $action остается не полоной ($action = "сlo"). Как результат — для нас ничего не изменится и мы будем попадать на сайт.

Для того, чтобы модифицированный конфиг вступил в силу рестартуем веб-сервер

sudo /etc/init.d/nginx restart

Модуль ngx_http_geo_module имеет множество дополнительных «плюшек». Например, есть возможность подгружать список с адресами и значениями из отдельного файла. Это позволит быстро изменять список исключения, не меняя конфиг Nginx. За подробностями прошу в документацию.

На этом настройка окончена.
Использование:

  • Закрыть доступ: touch /etc/nginx/maintenance.file
  • Открыть доступ: rm /etc/nginx/maintenance.file

Очень надеюсь, что изложенный выше способ будет кому-то полезен и сэкономит время.

Автор: maffei

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js