1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7

# теория · шаг 1 из 7

allow и deny: порядок и наследование

Коротко

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

  • Первое подошедшее правило выигрывает. deny all; первой строкой делает все allow ниже недостижимыми.
  • Не подошло ни одно правило - запрос проходит. Забыл deny all; в конце - список не ограничивает никого.
  • Наследование целиком или никак: своя строка в location выбрасывает весь внешний список. Замер: сайт с deny all на уровне server отдал 200.
  • return в том же блоке выполняется раньше проверки доступа.

Если помнишь про порядок правил и про наследование «всё или ничего» - листай до «Теперь сам».

Сначала ответь сам

/etc/nginx/conf.d/shop.local.confhttp
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/
↳  выводcurl с адреса вне 10.0.0.0/8
200

Двести. Сайт открыт. Правило deny all для этого location не действует вовсе: список наследуется только тогда, когда на текущем уровне нет ни одной своей строки. Появилась своя - внешний список выброшен целиком, а собственный на этот адрес не сработал, и запрос прошёл по правилу «ни одно правило не подошло».

На что это похоже

Инструкция охраннику: «пускать по списку». Списков два - общий на входе в здание и свой на этаже. Правило простое и опасное: если у этажа есть свой список, общий не смотрят вовсе.

Дописали на этаже одну фамилию - и общий запрет для этого этажа перестал существовать. Ровно это и происходит в конфиге, который выглядит закрытым.

Механизм: два свойства списка

порядок allow 192.168.1.0/24; deny all; закрыто всем, кроме офиса deny all; allow 192.168.1.0/24; закрыто всем: строка allow недостижима наследование своя строка в location выбрасывает весь внешний список дописать одно правило к унаследованным нельзя
Список читается сверху вниз до первого совпадения; уровни не складываются.

Правило

/etc/nginx/conf.d/shop.local.confhttp › server › location /admin/
allow 192.168.1.0/24;
deny  all;

Полный список на каждом уровне, где вообще есть ограничения. Никакого способа «дописать одно правило к унаследованным» в этом модуле нет.

Замер трёх вариантов на одном стенде:

что написано ответ с моего адреса
deny all; затем allow мой-адрес; 403 - allow недостижим
allow мой-адрес; затем deny all; 200
allow чужой-адрес; без deny all 200 - ни одно правило не подошло

Третья строка - самая коварная: конфиг выглядит как ограничение, а не ограничивает никого.

Разбор: что видит отвергнутый

Отказ - это 403 и строка в логе:

↳  выводerror.log
[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»; знак = берёт итоговый код из него):

/etc/nginx/conf.d/shop.local.confhttp › server › location /admin/
allow 192.168.1.0/24;
deny  all;
error_page 403 = @silence;
/etc/nginx/conf.d/shop.local.confhttp › server
location @silence { return 404; }

Отдавать 404 вместо 403 - осмысленный приём для админки: сканеру интернета не за что зацепиться, 403 однозначно говорит «тут что-то есть».

Разбор: доступ проверяется не первым

Порядок фазРазбирается в разделе 13, глава «return против rewrite»: Фазы: кто и когда решает из тринадцатого раздела действует и здесь:

/etc/nginx/conf.d/shop.local.confhttp › server › location /admin/
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 - запрос пароля, следующий урок), и при этом «или», а не «и»:

/etc/nginx/conf.d/shop.local.confhttp › server › location /admin/
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 заканчивает поиск на первом совпадении; без совпадения он разрешает запрос. Проверь порядок правил, завершающий запрет и наследование во вложенных блоках. Затем убедись, что обработка доходит до фазы доступа: ранний return способен завершить запрос раньше.

Комментарии

Пока нет комментариев. Будь первым!

Оставить комментарий

Комментарий появится после проверки. Email не публикуется. Войти, чтобы не вводить имя каждый раз.