# теория · шаг 1 из 7
allow и deny: порядок и наследование
Коротко
Самый дешёвый способ закрыть админку - не пускать в неё из интернета. Модуль крошечный, а ошибок вокруг него много, и все из-за двух свойств.
- Первое подошедшее правило выигрывает.
deny all;первой строкой делает всеallowниже недостижимыми. - Не подошло ни одно правило - запрос проходит. Забыл
deny all;в конце - список не ограничивает никого. - Наследование целиком или никак: своя строка в
locationвыбрасывает весь внешний список. Замер: сайт сdeny allна уровнеserverотдал 200. returnв том же блоке выполняется раньше проверки доступа.
Если помнишь про порядок правил и про наследование «всё или ничего» - листай до «Теперь сам».
Сначала ответь сам
server {
listen 80;
deny all; # закрыли весь сайт
location /health/ {
allow 10.0.0.0/8; # мониторингу можно
}
}
Что получит запрос к /health/ с чужого адреса 203.0.113.5?
curl -s -o /dev/null -w '%{http_code}\n' http://shop.local/health/
200
Двести. Сайт открыт. Правило deny all для этого location не действует вовсе:
список наследуется только тогда, когда на текущем уровне нет ни одной своей
строки. Появилась своя - внешний список выброшен целиком, а собственный на этот
адрес не сработал, и запрос прошёл по правилу «ни одно правило не подошло».
На что это похоже
Инструкция охраннику: «пускать по списку». Списков два - общий на входе в здание и свой на этаже. Правило простое и опасное: если у этажа есть свой список, общий не смотрят вовсе.
Дописали на этаже одну фамилию - и общий запрет для этого этажа перестал существовать. Ровно это и происходит в конфиге, который выглядит закрытым.
Механизм: два свойства списка
Правило
allow 192.168.1.0/24;
deny all;
Полный список на каждом уровне, где вообще есть ограничения. Никакого способа «дописать одно правило к унаследованным» в этом модуле нет.
Замер трёх вариантов на одном стенде:
| что написано | ответ с моего адреса |
|---|---|
deny all; затем allow мой-адрес; |
403 - allow недостижим |
allow мой-адрес; затем deny all; |
200 |
allow чужой-адрес; без deny all |
200 - ни одно правило не подошло |
Третья строка - самая коварная: конфиг выглядит как ограничение, а не ограничивает никого.
Разбор: что видит отвергнутый
Отказ - это 403 и строка в логе:
[error] access forbidden by rule, client: 203.0.113.5, request: "GET /admin/ HTTP/1.1"
Формулировка «by rule» отличает отказ по allow/deny от отказа по правам на
файл (там будет Permission denied) и от отказа приложения. Если в логе этой
строки нет, а клиент получает 403 - смотри не сюда.
Свой ответ вместо стандартного делают через error_page (403 = @silence -
«код 403 обслужи именованным блоком @silence»; знак = берёт итоговый код из
него):
allow 192.168.1.0/24;
deny all;
error_page 403 = @silence;
location @silence { return 404; }
Отдавать 404 вместо 403 - осмысленный приём для админки: сканеру интернета не за что зацепиться, 403 однозначно говорит «тут что-то есть».
Разбор: доступ проверяется не первым
Порядок фазРазбирается в разделе 13, глава «return против rewrite»: Фазы: кто и когда решает из тринадцатого раздела действует и здесь:
deny all;
return 200 "ok\n";
Такой блок никого не ограничивает: return выполняется на фазе rewrite,
а deny - на фазе access, то есть позже. Клиент получит 200.
Ловушка не теоретическая: так пишут заглушки для проверки правил, получают 200 и делают вывод, что модуль не работает. Проверять надо на том, что отдаёт файл или уходит на бэкенд.
Что ломается без этого
Адрес за прокси. allow 192.168.1.0/24; смотрит на $remote_addr. За CDN
это адрес CDN: правило либо не пускает никого, либо пускает всех, кто пришёл
через тот же CDN, - то есть весь интернет. Сначала realip из седьмого раздела,
потом allow.
IPv6, о котором забыли. allow 192.168.1.0/24; deny all; закрывает офис от
самого себя, если тот пришёл по IPv6: адрес не подошёл ни под одно правило,
сработал deny all. Диагностируется мгновенно - в логе будет адрес вида
2a02:....
Зачем это в работе
allow/deny - первый рубеж, а не единственный. Он ничего не знает о том, кто
человек: адрес не аутентификация. Домашний интернет сотрудника, командировка,
мобильный - и правило начинает мешать работе, а не защищать.
Поэтому типовая схема на две ступени: адрес плюс пароль
(auth_basicРазбирается в разделе 12, глава «Доступ: allow, deny, auth_basic, auth_request»: auth_basic и satisfy - запрос пароля, следующий урок), и при
этом «или», а не «и»:
satisfy any;
allow 192.168.1.0/24;
deny all;
auth_basic "admin";
auth_basic_user_file /etc/nginx/htpasswd;
satisfy any означает «достаточно одного условия из списка» (по умолчанию
all - нужны все): из офиса пускает без пароля, снаружи спрашивает пароль. Про
satisfy и auth_basic - следующий урок.
Теперь сам
В конфиге на уровне server стоит deny all;, а в location /static/ -
expires 30d;. Статика открывается из интернета. Это ошибка наследования?
Нет. expires - не директива доступа, своих allow/deny в блоке нет, значит
внешний список наследуется целиком и deny all действует. Если статика всё-таки
открывается, ищи другой location, который её перехватывает - например,
регулярку по расширению, объявленную выше и со своим allow.
Главное
Список allow/deny читается сверху вниз до первого совпадения: deny all
первой строкой делает всё ниже недостижимым. Если не подошло ни одно правило,
запрос проходит - поэтому список почти всегда заканчивается deny all.
Наследование целиком или никак: своя строка в location выбрасывает внешний
список, и замер это показывает - сайт с deny all на уровне server отдаёт 200
там, где в блоке написан один allow. Проверка доступа идёт позже return, а
за прокси она смотрит на адрес прокси, пока не настроен realip.
Комментарии
Пока нет комментариев. Будь первым!
Оставить комментарий