Почему доверенные IP попадают под блокировки и как работает whitelist
Белый список IP (whitelist) - это правило, которое пропускает доверенный адрес раньше, чем срабатывает блокировка. В iptables и nftables пакет идёт по цепочке сверху вниз до первого совпадения (first-match), поэтому ACCEPT для администратора обязан стоять выше DROP и REJECT. Правило, добавленное в конец цепочки, не защищает: трафик до него уже отброшен.
Уровней фильтрации несколько. Запрос последовательно проходит Cloudflare, Nginx, Fail2ban и пакетный фильтр ядра. Исключение в Fail2ban не отменяет запрет в Nginx, а allow в Nginx бесполезен, если пакеты режет iptables. Whitelist настраивают на каждом уровне, где включена блокировка.
Схема обработки: Cloudflare → Nginx → Fail2ban → iptables/nftables. Чем ближе к ядру, тем меньше информации о запросе, но тем раньше отбрасывается трафик.
Для первичной диагностики хватает четырёх команд:
iptables -L -v -n nft list ruleset fail2ban-client status tail -f /var/log/nginx/error.log
Команды и конфиги ниже рассчитаны на iptables-legacy и iptables-nft, nftables, Fail2ban 0.11+, Nginx 1.18+. Если firewall на сервере только планируется, начните с готовых конфигураций в материале про настройку файрвола на Linux-сервере.
Порядок обработки цепочек: почему ACCEPT должен быть выше DROP
Типичная ошибка выглядит так: сначала добавляют блокирующее правило, затем исключение для доверенного адреса.
iptables -A INPUT -j DROP iptables -A INPUT -s 203.0.113.10 -j ACCEPT
Первый пакет от 203.0.113.10 попадёт под DROP и до ACCEPT не дойдёт. Флаг -A добавляет правило в конец цепочки, что для whitelist почти всегда неверно. Рабочий вариант - вставка первой строкой:
iptables -I INPUT 1 -s 203.0.113.10 -j ACCEPT
В nftables логика та же, но различаются операторы: insert ставит правило в начало цепочки, add - в конец.
nft insert rule inet filter input ip saddr 203.0.113.10 accept nft add rule inet filter input ip saddr 203.0.113.10 accept
Проверьте фактический порядок и счётчики пакетов:
iptables -L INPUT -v -n --line-numbers nft list chain inet filter input
Если счётчик у правила ACCEPT растёт, адрес проходит фильтр. Нулевой счётчик при активном трафике означает, что правило стоит после блокирующего и не применяется. Не используйте -A для whitelist, если точно не знаете текущий порядок строк в цепочке.
Уровни фильтрации: где именно блокируется IP
Прежде чем что-то настраивать, определите уровень блокировки. Симптом подсказывает, где искать.
| Симптом | Уровень | Где смотреть |
|---|---|---|
| Нет ответа на ping, TCP-соединение не устанавливается | iptables/nftables или Cloudflare | iptables -L -v -n, nft list ruleset, IP Access Rules |
| Соединение есть, ответ 403 | Nginx | директивы allow/deny, error.log |
| Ответ 429 при рабочих лимитах | Nginx limit_req или Cloudflare Rate Limiting | limit_req_zone, логи Cloudflare |
| SSH подключается и сразу рвётся, либо бан после нескольких попыток | Fail2ban | fail2ban-client banned, /var/log/fail2ban.log |
| Запрос не доходит до сервера, отдаётся страница Cloudflare | Cloudflare WAF | Security Events в панели |
Whitelist на одном уровне не отменяет блокировку на другом. Какие инструменты блокировки применяются на практике и как они сочетаются, разобрано в статье про блокировку IP-адресов и практическую автоматизацию.
Whitelist в iptables: приоритет ACCEPT и готовые правила
Для одного адреса достаточно одной строки, вставленной в начало INPUT:
iptables -I INPUT 1 -s 203.0.113.10 -j ACCEPT
Для подсети администраторов:
iptables -I INPUT 1 -s 203.0.113.0/24 -j ACCEPT
Для нескольких адресов администраторов и систем мониторинга (Zabbix, Prometheus) вставьте каждое правило первой строкой. Порядок между самими whitelist-правилами не важен, важно, что все они выше блокирующих.
iptables -I INPUT 1 -s 203.0.113.10 -j ACCEPT iptables -I INPUT 1 -s 203.0.113.11 -j ACCEPT iptables -I INPUT 1 -s 198.51.100.0/24 -j ACCEPT
Проверьте, что правила встали наверх, и посмотрите счётчики:
iptables -L INPUT -v -n --line-numbers
Убедитесь, какой бэкенд используется в системе. В современных дистрибутивах команда iptables часто работает через nftables:
iptables --version
Если в выводе есть nf_tables, вы работаете с iptables-nft, и правила транслируются в nftables. Это важно учитывать при отладке, потому что часть правил может приходить из чужих цепочек.
Правила живут до перезагрузки. Сохраните их подходящим для дистрибутива способом:
iptables-save > /etc/iptables/rules.v4 service iptables save netfilter-persistent save
Первая команда подходит для Debian/Ubuntu с netfilter-persistent, вторая - для классических RHEL/CentOS. Без сохранения whitelist исчезнет после reboot вместе со всей конфигурацией.
Fail2ban добавляет свои цепочки (например, f2b-sshd) и вызывает их из INPUT. Правило ACCEPT в INPUT, стоящее выше вызова этих цепочек, пропускает адрес и мимо банов, но корректнее исключать адрес в самом Fail2ban через ignoreip.
Как проверить, что whitelist срабатывает раньше блокировки
Проверка строится на трёх шагах. Сначала убедитесь, что правило на месте и стоит первым:
iptables -L INPUT -v -n --line-numbers | head nft list chain inet filter input
Затем посмотрите счётчик конкретного адреса:
iptables -L -v -n | grep 203.0.113.10
Дальше проверьте доступ с доверенной машины:
curl -I https://example.com ssh admin@example.com
Если обе проверки проходят, whitelist в пакетном фильтре работает. Если curl отдаёт 403, а SSH работает, блокировка находится в Nginx, а не в iptables. Если не проходит SSH, ищите бан Fail2ban или правило DROP выше вашего ACCEPT.
Whitelist в nftables: синтаксис и порядок правил
В nftables whitelist добавляют оператором insert, чтобы правило оказалось в начале цепочки:
nft insert rule inet filter input ip saddr 203.0.113.10 accept nft insert rule inet filter input ip saddr 203.0.113.0/24 accept
Несколько адресов можно перечислить в одном правиле через множество:
nft insert rule inet filter input ip saddr { 203.0.113.10, 203.0.113.11 } accept
Оператор add добавляет правило в конец цепочки и для whitelist опасен тем же, чем -A в iptables. Если нужно поставить правило в конкретную позицию, nftables позволяет сослаться на handle существующего правила: insert rule с position вставляет новое правило перед указанным, add rule с position - после него.
nft insert rule filter output position 8 ip daddr 127.0.0.8 drop
Проверить порядок и убедиться, что правило первое:
nft list chain inet filter input nft list ruleset
Имена таблицы и цепочки зависят от дистрибутива и активного firewall. Встречаются inet filter input, inet firewalld filter_INPUT и другие. Перед вставкой правила выполните nft list ruleset и уточните фактические названия, иначе команда вернёт ошибку No such file or directory.
Сохранение между перезагрузками:
nft list ruleset > /etc/nftables.conf systemctl enable --now nftables
Как избежать конфликта iptables и nftables
Определите, какой инструмент реально управляет фильтром:
iptables --version update-alternatives --display iptables nft list ruleset
При бэкенде iptables-nft команды iptables транслируются в правила nftables, поэтому iptables -L и nft list ruleset показывают одни и те же данные в разном виде. Прямые nftables-правила и цепочки от iptables сосуществуют, но разобраться в приоритетах между ними сложно.
Практическое правило: выберите один инструмент для управления фильтром на сервере. Смешение iptables и прямых правил nftables на одной машине приводит к ситуации, когда whitelist добавлен, счётчик растёт, а блокировка всё равно срабатывает из цепочки, о которой вы забыли.
Fail2ban: параметр ignoreip и проверка исключений
Fail2ban исключает адреса параметром ignoreip. Готовый фрагмент /etc/fail2ban/jail.local:
[DEFAULT] ignoreip = 127.0.0.1/8 ::1 203.0.113.10 203.0.113.0/24 bantime = 1h findtime = 10m maxretry = 4 [sshd] enabled = true [nginx-http-auth] enabled = true
В ignoreip перечисляют отдельные адреса, подсети в нотации CIDR и адреса IPv6 через пробел. Локальный адрес и IPv6 loopback стоит оставить всегда, иначе сервисы на самом хосте начнут банить себя.
Проверка, что адрес попал в исключения:
fail2ban-client status sshd fail2ban-client banned fail2ban-client set sshd unbanip 203.0.113.10 fail2ban-client reload
В выводе fail2ban-client status sshd есть блок Ignored IP, и ваши адреса должны быть в нём. Команда fail2ban-client banned показывает текущие баны по всем jail. Если адрес уже забанен, снимите бан вручную и только потом проверяйте ignoreip. После правки конфига перезагрузите Fail2ban: reload применяет изменения без полного перезапуска.
Проверить, какие строки лога вообще попадают под фильтр, помогает fail2ban-regex с указанием файла лога и фильтра. Это отделяет проблему с ignoreip от проблемы с ложными срабатываниями фильтра.
ignoreip не отменяет блокировки на уровне iptables и nftables. Если Fail2ban пишет свои правила в пакетный фильтр, а перед ним стоит ваш DROP, доверенный адрес всё равно не пройдёт. Настраивайте исключения на обоих уровнях.
Глобальный и локальный ignoreip: что выбрать
Список в секции [DEFAULT] действует для всех jail сразу. Параметры из [DEFAULT] можно переопределить для конкретного jail как опции спецификации действия в этом jail. Практический вывод: если задать ignoreip внутри отдельного jail, для него начнёт действовать именно этот список, а не общий. Поэтому при дублировании одного адреса в [sshd] остальные адреса из [DEFAULT] для этого jail перестанут учитываться.
Схема, которая не ломается при росте конфига: общий список доверенных адресов держите в [DEFAULT], а внутри jail добавляйте только специфичные адреса и дублируйте общий перечень целиком. Например, [nginx-http-auth] с ignoreip = 127.0.0.1/8 ::1 203.0.113.10 203.0.113.0/24 198.51.100.5.
Nginx: allow/deny и защита от блокировок на уровне веб-сервера
Директивы allow и deny обрабатываются в порядке следования. Доверенные адреса перечисляют выше deny all:
location /admin {
allow 203.0.113.10;
allow 203.0.113.0/24;
deny all;
}
Если deny all поставить первым, доверенный адрес получит 403, потому что совпадение уже найдено. Проверка конфига и результата:
nginx -t curl -I https://example.com/admin
Отдельный случай - лимиты запросов. Модуль limit_req и директива limit_conn не смотрят на allow/deny и режут трафик по частоте. Админский адрес или система мониторинга легко попадает под 429 при выгрузке метрик или массовой проверке доступности. Исключение строится через карту, где доверенной подсети присваивается пустой ключ:
geo $limited {
default 1;
203.0.113.0/24 0;
}
map $limited $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=api:10m rate=10r/s;
Пустой ключ в limit_req_zone означает, что лимит для этого адреса не применяется - это штатное поведение nginx, а не обходной приём. Как лимиты сочетаются с фильтрацией на сетевом уровне, показано в руководстве по многоуровневой защите от DDoS.
Как учесть реальный IP за Cloudflare в Nginx
За Cloudflare в $remote_addr окажется адрес edge-сервера, а не адрес администратора. Директивы allow/deny в этом случае сработают не по тому IP. Включите модуль realip и укажите, каким подсетям доверять:
set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; real_ip_header CF-Connecting-IP;
Подсети в примере приведены выборочно, полный и актуальный перечень публикует Cloudflare в официальной документации и отдаёт по API. Список меняется, поэтому его стоит обновлять по расписанию, а не переносить один раз: если его «вписать и забыть», realip перестанет работать или начнёт отбрасываться легитимный трафик из новых диапазонов.
При real_ip_recursive on Nginx идёт по цепочке forwarded-адресов справа налево и выбирает последний адрес, не входящий в доверенный диапазон прокси. Это полезно, когда перед Cloudflare стоит ещё один прокси.
Проверка: добавьте в формат лога обе переменные и сравните значения при заходе с доверенного адреса.
log_format debug '$http_cf_connecting_ip $remote_addr $request';
После корректной настройки realip значение $remote_addr совпадёт с реальным адресом клиента, и allow/deny начнут работать предсказуемо.
Cloudflare: IP Access Rules и обход блокировок
Whitelist на периметре Cloudflare настраивают двумя способами: IP Access Rules с действием Allow и Custom Rules в WAF с действием Skip.
IP Access Rules позволяют создавать allowlist, block и challenge по IP-адресу, ASN или стране посетителя. Allow для IP или ASN обходит настроенные custom rules, rate limiting rules, WAF Managed Rules и firewall rules (deprecated). Правило добавляют в разделе безопасности панели: адрес или подсеть плюс действие Allow. Подробнее об этом - в документации Cloudflare по IP Access Rules.
Важная оговорка: Cloudflare рекомендует вместо IP Access Rules использовать custom rules для IP- или гео-блокировок. При переходе на custom rules действие block из IP Access Rules стоит заменять на действие block, которое не обходит все средства защиты приложений Cloudflare. То есть Allow в IP Access Rules - это широкое исключение, и применять его нужно осознанно.
Rate limiting rules оцениваются по порядку, и некоторые действия, например block, останавливают оценку других правил. Поэтому проверяйте Rate Limiting отдельно: если админский скрипт генерирует много запросов, счётчик лимита может продолжить расти, и адрес попадёт под 429. Порядок оценки описан в документации Cloudflare по rate limiting rules.
Второй вариант - Custom Rule в WAF с выражением по адресу и действием Skip для оставшихся правил. Так исключение обходит и managed-наборы, которые вы не контролируете напрямую:
ip.src eq 203.0.113.10
Любые настройки Cloudflare не отменяют фильтрацию на origin. Трафик, дошедший до сервера, продолжат проверять Nginx и iptables. Сравнение периметровых решений смотрите в статье про геофильтрацию в Cloudflare, AWS WAF и Nginx.
Cloudflare Access и Tunnel как альтернатива whitelist
Статический список IP плохо подходит администраторам с динамическими адресами и сотрудникам на домашнем интернете. Вместо расширения whitelist закройте админские пути аутентификацией: Cloudflare Access проверяет вход через SSO, и IP-адрес перестаёт быть фактором доступа.
Cloudflare Tunnel убирает необходимость открывать входящие порты на сервере. Типовая последовательность команд:
cloudflared tunnel create admin-wiki cloudflared tunnel route dns admin-wiki admin.example.com cloudflared tunnel run admin-wiki
Ограничение честное: системы мониторинга обычно не проходят SSO, и для них whitelist по IP остаётся рабочим вариантом. Для Cloudflare Access это значит, что исключения по адресу всё равно придётся держать для Zabbix, Prometheus и подобных агентов.
Типичные ошибки: почему белый список не работает
Пять причин закрывают почти все случаи, когда правило добавлено, но доверенный адрес всё равно блокируется.
- Правило ACCEPT добавлено в конец цепочки. Флаг -A в iptables и оператор add в nftables ставят его после блокирующих правил. Исправление: iptables -I INPUT 1 или nft insert rule inet filter input.
- Конфликт iptables и nftables. На сервере с бэкендом iptables-nft правила живут в двух представлениях, и блокировка приходит из цепочки, которую не видно в выводе iptables -L. Проверьте iptables --version и nft list ruleset.
- ignoreip не сработал. Причины: адрес продублирован в jail и перекрыл глобальный список, опечатка в CIDR, конфиг не перезагружен. Проверьте fail2ban-client status sshd и /var/log/fail2ban.log.
- В Nginx deny all стоит выше allow. Порядок директив внутри location и server решает результат, первое совпадение выигрывает.
- IP заблокирован на периметре, а whitelist настроен только на сервере. Запрос не доходит до Nginx, поэтому в логах его нет. Смотрите IP Access Rules, WAF и Rate Limiting в панели Cloudflare.
Чек-лист диагностики: где именно блокируется IP
- С доверенного адреса выполните ping и curl -I. Отсутствие ответа указывает на сетевой уровень или периметр, ответ 403 или 429 - на прикладной.
- Проверьте пакетный фильтр: iptables -L -v -n --line-numbers, nft list ruleset. Смотрите счётчики у блокирующих правил.
- Проверьте Fail2ban: fail2ban-client status, fail2ban-client banned, лог /var/log/fail2ban.log.
- Проверьте логи веб-сервера: tail -f /var/log/nginx/error.log и access.log. Если запроса нет, блокировка выше Nginx.
- Проверьте периметр: IP Access Rules, Custom Rules WAF, Rate Limiting и Security Events.
- Снимите трафик на интерфейсе: tcpdump -i eth0 host 203.0.113.10. Наличие SYN без ответа подтверждает отброс на сетевом уровне.
Безопасность whitelist: как не открыть лишнего
Whitelist расширяет поверхность атаки ровно настолько, насколько широк список. Держите принцип минимальных привилегий: только те адреса, без которых работа останавливается, и только на нужные порты.
Ограничение по портам для системы мониторинга выглядит так:
iptables -I INPUT 1 -s 203.0.113.10 -p tcp --dport 10050 -j ACCEPT
Адрес Zabbix-сервера получает доступ только к агенту на порту 10050, а не ко всем сервисам. Такой же подход применяйте к Prometheus, сборщикам логов и CI/CD-раннерам.
Не добавляйте 0.0.0.0/0 и не расширяйте подсети без причины. Каждый лишний диапазон - это чужие хосты, которые получат ваши привилегии при смене владельца адреса.
Для администраторов с динамическими адресами используйте VPN с обязательным вторым фактором или jump host с фиксированным адресом. Тогда в whitelist попадает один адрес шлюза, а не список домашних сетей сотрудников.
Храните перечень доверенных адресов в git или в ролях Ansible, а не только в живых правилах на сервере. Так список не потеряется при переустановке, а изменения проходят ревью. Общая рамка работы с доступом на сервере описана в руководстве по управлению доступом и защите Linux-сервера.
Как защитить whitelist от компрометации
Адрес из whitelist получает доступ в обход защиты. Если машину администратора скомпрометировали, злоумышленник наследует это доверие. Логирование попаданий даёт заметить аномалии:
iptables -I INPUT 1 -s 203.0.113.10 -j LOG --log-prefix "WHITELIST: "
Правило LOG ставят рядом с ACCEPT, чтобы видеть объём трафика с доверенных адресов. Всплеск в нерабочее время или нехарактерные порты - повод проверить машину.
Для админских путей в Nginx добавьте второй слой: basic auth, клиентские сертификаты или auth_request к SSO. Тогда одного IP для доступа недостаточно. В Cloudflare предпочтительнее Access вместо статического списка: он проверяет пользователя, а не адрес.
Раз в квартал пересматривайте список: iptables -L -v -n, nft list ruleset, fail2ban-client status, конфиги Nginx. Правило ACCEPT с нулевым счётчиком пакетов за месяц стоит удалить.
Чек-лист: настройка whitelist на всех уровнях
- Соберите список доверенных адресов: администраторы, системы мониторинга, CI/CD, офисные VPN.
- iptables или nftables: добавьте ACCEPT первой строкой INPUT, проверьте iptables -L INPUT -v -n --line-numbers.
- Fail2ban: пропишите ignoreip в /etc/fail2ban/jail.local и примените fail2ban-client reload.
- Nginx: поставьте allow выше deny all, при работе за Cloudflare включите realip и real_ip_header CF-Connecting-IP.
- Cloudflare: добавьте IP Access Rules с действием Allow или Custom Rule WAF со Skip, отдельно проверьте Rate Limiting.
- Проверьте доступ с доверенного адреса: ping, curl -I, ssh.
- Проверьте логи всех уровней: iptables -L -v -n, fail2ban-client status, /var/log/nginx/error.log, Security Events.
- Сохраните правила: iptables-save, nft list ruleset > /etc/nftables.conf, конфиги в git или Ansible.
- Запланируйте аудит списка и удаление адресов, по которым нет трафика.
Главное правило остаётся неизменным: whitelist должен обрабатываться раньше блокирующих правил на каждом уровне. Проверяйте это счётчиками пакетов и логами, а не по памяти.