Совсем недавно возникла интересная задача: реализовать закрытие доступа к веб-сайту из вне, на время технических работ. Мне показалось, что это довольно распространенная задачка, решение которой заинтересует многих.
Один из возможных вариантов решения — ниже.
Дано
- Сервер: 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
уже существует. - Так как триггер-файл создан:
$tmp = "clo"
- Айпишник не из списка исключений:
$maintenance = "yes"
- Поскольку
$maintenance = "yes"
, то:$action = $tmp + "se" = "close"
$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
Очень надеюсь, что изложенный выше способ будет кому-то полезен и сэкономит время.