Чужие правила firewall: как разобрать iptables, который писали пять лет
Вы открываете iptables-save на сервере, который достался вам по наследству, и видите несколько сотен строк без единого комментария. Часть правил явно дублируется, часть противоречит друг другу, а часть, кажется, не срабатывала уже пару лет. Автора нет, документации нет, а трогать что-то страшно — одна неверная строка, и вы сами себе закроете доступ по SSH. Разберём, как методично прочитать чужой firewall, понять его логику и безопасно внести изменения, даже если правила писали пять разных админов за пять лет.
Содержание
- Сначала — снимите слепок текущих правил, не полагаясь на память сервера
- Как читать порядок и приоритет: таблицы, цепочки, first match wins
- Невидимые соавторы: кто ещё пишет в ваш firewall без предупреждения
- Поиск дублей и противоречий в накопленных правилах
- Восстановление смысла: зачем это правило вообще тут
- Тестирование изменений без риска потерять доступ
- Переход от «разобрались» к управляемому состоянию
Сначала — снимите слепок текущих правил, не полагаясь на память сервера
Прежде чем разбирать логику, зафиксируйте состояние как есть — это и точка отката, и материал для анализа офлайн.
mkdir -p /root/fw-audit && cd /root/fw-audit
iptables-save -c > iptables-$(date +%F).rules
ip6tables-save -c > ip6tables-$(date +%F).rules
Флаг -c сохраняет счётчики пакетов и байт вместе с правилами — без него вы не увидите, срабатывает ли правило вообще. Про IPv6 забывают почти всегда: если ip6tables не трогали годами, там вполне может висеть ACCEPT по умолчанию, пока iptables жёстко фильтрует IPv4.
Дальше проверьте, какой бэкенд у вас на самом деле работает — это критично для интерпретации всего остального:
iptables --version
# iptables v1.8.9 (nf_tables) — это обёртка над nftables
# iptables v1.8.9 (legacy) — это классический netfilter
update-alternatives --list iptables 2>/dev/null
nft list ruleset > nft-ruleset-$(date +%F).txt
Грабля пяти лет эксплуатации в том, что на сервере может параллельно жить и классический iptables-legacy, и nftables — ядро применяет оба набора независимо, и они не видят правила друг друга. Если iptables-save пуст, а трафик всё равно режется, смотрите nft list ruleset, и наоборот.
Отдельно найдите, откуда правила берутся при перезагрузке — иначе любой аудит будет неполным:
systemctl status netfilter-persistent 2>/dev/null
cat /etc/iptables/rules.v4 /etc/iptables/rules.v6 2>/dev/null
cat /etc/nftables.conf 2>/dev/null
grep -rl iptables /etc/network/if-up.d/ /etc/rc.local /etc/cron.d/ /etc/systemd/system/*.service 2>/dev/null
Часто реальные правила — не то, что лежит в /etc/iptables/rules.v4, а то, что поверх накатил systemd-юнит или cron-скрипт. Разница между «сохранённым» и «применённым» состоянием — источник половины сюрпризов при аудите легаси-firewall.
Как читать порядок и приоритет: таблицы, цепочки, first match wins
Firewall на базе netfilter устроен как набор таблиц (filter, nat, mangle, raw) и цепочек (INPUT, FORWARD, OUTPUT, PREROUTING, POSTROUTING). Внутри каждой цепочки правила проверяются строго сверху вниз, и первое совпавшее правило с финальным действием (ACCEPT, DROP, REJECT) останавливает обработку — если только это не RETURN в дочернюю цепочку, тогда управление уходит обратно в родительскую.
Чтобы увидеть реальный порядок с номерами строк и счётчиками:
iptables -L INPUT -v -n --line-numbers
iptables -S INPUT # тот же список в виде команд iptables -A ...
Второй вариант удобнее для копирования и грепа: он показывает правила ровно в том синтаксисе, которым их можно было бы воссоздать. Обязательно посмотрите политику по умолчанию — это последняя строка логики, которую легко упустить:
iptables -S INPUT | head -1
# -P INPUT DROP — всё, что не совпало явно, отбрасывается
# -P INPUT ACCEPT — всё, что не совпало явно, пропускается
Сервер с политикой ACCEPT и десятком DROP-правил в середине списка устроен ровно наоборот по логике, чем сервер с политикой DROP и явными ACCEPT в начале. На пятилетнем легаси оба подхода нередко встречаются вперемешку в разных цепочках — зафиксируйте это явно, иначе легко перепутать «правило блокирует» с «правило разрешает».
Кастомные цепочки — второй слой, который усложняет чтение. Если в INPUT есть -j SOME-CHAIN, логика продолжается в этой цепочке, и обработка может вернуться обратно через RETURN или уйти в DROP/ACCEPT внутри неё:
iptables -L -n | grep '^Chain' | grep -v -E 'INPUT|OUTPUT|FORWARD'
iptables -S SOME-CHAIN
Постройте граф переходов для нестандартных цепочек — хотя бы в текстовом файле — прежде чем менять что-то внутри них. В nftables то же самое читается как приоритеты хуков:
nft list chain inet filter input
# type filter hook input priority 0; policy drop;
Число в priority определяет порядок между разными таблицами на одном хуке — меньшее число обрабатывается раньше. Если несколько таблиц nftables висят на hook input с разными приоритетами, порядок между ними важнее порядка правил внутри одной таблицы.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНевидимые соавторы: кто ещё пишет в ваш firewall без предупреждения
Прежде чем считать правило «непонятным легаси», проверьте, не создаёт ли его автоматически один из системных сервисов — иначе потратите час на разгадку того, что генерируется заново при каждом рестарте демона.
Docker вставляет собственные цепочки DOCKER, DOCKER-USER, DOCKER-ISOLATION-STAGE-1/2 в таблицы nat и filter при каждом старте демона — правила из docker-compose с ports: открывают порты в обход даже строгой политики INPUT, потому что попадают в PREROUTING/FORWARD раньше пользовательских правил. Если на сервере есть контейнеры, отдельно смотрите:
iptables -t nat -L DOCKER -n
iptables -L DOCKER-USER -n -v
Подробнее о том, почему это регулярно приводит к случайно открытым базам, разобрано в статье про то, как Docker переписал iptables и открыл базу в интернет — там про последствия, здесь — про то, как эти правила опознать при аудите.
fail2ban создаёт динамические цепочки вида f2b-sshd, f2b-nginx-http-auth с временными банами:
iptables -L -n | grep f2b
fail2ban-client status
fail2ban-client status sshd
Эти правила меняются сами каждые несколько минут — не принимайте разницу между снимками за «кто-то поправил вручную».
UFW, если включён, оборачивает iptables своими цепочками ufw-user-input, ufw-before-input и хранит исходники в /etc/ufw/*.rules — отдельный слой абстракции поверх того же netfilter:
ufw status verbose
grep -c '^-A' /etc/ufw/user.rules
Проверьте также crontab на скрипты, которые сами дописывают правила по расписанию — частый способ появления «призрачных» блокировок абузеров:
crontab -l -u root | grep -i iptables
grep -rl iptables /etc/cron.* 2>/dev/null
Поиск дублей и противоречий в накопленных правилах
Когда правила писали несколько человек за несколько лет, дубли и конфликты почти гарантированы. Начните с точных дублей текста правила:
iptables-save | grep '^-A' | sed 's/\[[0-9]*:[0-9]*\] //' | sort | uniq -c | sort -rn | awk '$1>1'
Это покажет строки, встречающиеся больше одного раза буквально — часто результат того, что скрипт применения правил запускали повторно без проверки идемпотентности.
Дальше — «мёртвые» правила по счётчикам. Если аптайм большой, а счётчик пакетов у правила нулевой, это кандидат на удаление, но не доказательство:
iptables -L -v -n | awk '$1==0 && $2==0 {print}'
uptime -s
Ноль совпадений может значить как «правило не нужно», так и «правило защищает от события, которое ещё не случилось». Не удаляйте по одному нулевому счётчику, особенно если рядом есть комментарий -m comment --comment — это единственная официальная память о причине правила, ищите такие отдельно:
iptables-save | grep -o 'comment "[^"]*"' | sort | uniq -c
Противоречия сложнее автоматизировать, но для конкретного адреса или порта это делается прямым grep по правилам всех цепочек:
iptables-save | grep '203.0.113.'
Если для одного адреса встречаются и ACCEPT, и DROP в разных цепочках или строчках — выигрывает то, что раньше по порядку (см. предыдущий раздел). Отдельный признак технического долга — десятки индивидуальных правил -s X.X.X.X -j DROP вместо одного ipset. Проверьте, есть ли уже настроенный набор:
ipset list -t
Если ipset не используется, а список IP растёт построчно годами — это уже не проблема логики, а вопрос производительности: линейный перебор сотен -s-условий заметно дороже одного хэш-поиска.
Восстановление смысла: зачем это правило вообще тут
Часть правил не объясняется ни комментарием, ни очевидной логикой. Прежде чем удалять непонятное правило, соберите косвенные улики:
- Логи ядра. Если у правила есть
-j LOG --log-prefix "..."перед финальным действием, ищите совпадения вjournalctl -k— это покажет, ловит ли правило что-то прямо сейчас. - conntrack.
conntrack -L | grep <IP или порт>покажет, использует ли кто-то прямо сейчас то, что правило разрешает. - История и конфигурация.
cat /root/.bash_history | grep -i iptablesи поиск систем управления конфигурацией (/etc/ansible,/etc/puppet,/etc/salt) иногда находят автора и дату быстрее, чем анализ самих правил. - Даты файлов.
stat /etc/iptables/rules.v4покажет, когда файл правился в последний раз — если три года назад, а сервис давно снесён, шансы, что правило мёртвое, выше.
Если после всего этого правило всё ещё непонятно, не удаляйте его сразу — временно добавьте логирование перед ним с уникальным префиксом и понаблюдайте несколько дней или недель:
iptables -I INPUT <номер_перед_правилом> -j LOG --log-prefix "AUDIT-CHECK-1: " --log-level 4
journalctl -k -f | grep AUDIT-CHECK-1
Если за разумный срок совпадений нет — это весомый аргумент за удаление, но уже подкреплённый наблюдением, а не догадкой.
Тестирование изменений без риска потерять доступ
Это правило номер один при работе с чужим firewall: никогда не применяйте изменение, способное оборвать текущую сессию, без сетки безопасности. О том, как выглядит обратная ситуация, разобрано в статье про ошибку в правиле firewall, которая обрывает SSH-доступ — там же аварийное восстановление через консоль хостера.
Базовая техника — откат по таймеру через at, который сработает независимо от того, жива ли ваша сессия:
iptables-save -c > /root/fw-audit/before-change.rules
echo "iptables-restore < /root/fw-audit/before-change.rules" | at now + 10 minutes
# вносите изменение
# если всё в порядке — снимаете задачу
atq
atrm <номер_задачи>
Подтвердили, что доступ жив, — удалите отложенную задачу. Сессия оборвалась — через 10 минут правила откатятся сами. То же самое можно сделать через systemd-run, если at не установлен:
systemd-run --on-active=600 --unit=fw-rollback \
/bin/bash -c "iptables-restore < /root/fw-audit/before-change.rules"
Для Debian/Ubuntu есть готовый iptables-apply из пакета iptables-persistent — он применяет новый набор и просит подтверждение; не подтвердите за отведённое время — правила откатятся автоматически:
iptables-apply -t 30 /root/fw-audit/new.rules
Для nftables прямого аналога с автооткатом нет, но есть проверка синтаксиса без применения — она не спасёт от логической ошибки, но отловит опечатку:
nft -c -f /root/fw-audit/new-ruleset.nft
И обязательное — держите открытым второе, независимое SSH-соединение до начала изменений. Firewall обычно не рвёт уже установленные (ESTABLISHED) соединения при добавлении нового правила — рвутся только новые попытки подключения. Не полагайтесь на это как на единственную защиту, но вместе с таймером через at это закрывает почти все сценарии потери доступа.
Переход от «разобрались» к управляемому состоянию
Понять логику пятилетнего firewall — это половина работы. Вторая половина — сделать так, чтобы через год не пришлось повторять весь аудит с нуля.
- Свести правила в один читаемый источник. Если на сервере одновременно живут ручные правила
iptables, обёрткаufwи динамикаfail2ban, решите — это осознанное разделение (ufwдля входящих,fail2banдля банов) или исторический хаос, который стоит унифицировать под один инструмент. Тащить всё в nftables ради самой миграции не обязательно, если текущая связка работает предсказуемо. - Взять правила под версионный контроль. Простой
git initв/etc/iptables/с коммитом после каждого осознанного изменения решает большую часть проблемы «кто и зачем это добавил». - Подписывать новые правила комментарием.
-m comment --comment "JIRA-123, порт для CI-раннера, 2026-08"стоит секунды и экономит часы при следующем аудите. Как выстроить процесс, чтобы это соблюдалось, разобрано в статье про регламент изменений в firewall.
Аудит легаси-firewall — не разовая акция, а первый проход методики, которую стоит повторять при смене команды или крупном изменении инфраструктуры. Общий подход к обследованию сервера целиком описан в статье с чего начать разбор незнакомого сервера — firewall там один из слоёв, но часто самый рискованный для трогания вслепую.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Можно ли просто снести все правила и настроить firewall заново?
Технически да, но оправдано только если вы уверены, что знаете все сервисы и клиентов, которым нужен доступ. На проде с внешними интеграциями лучше сначала провести аудит по описанной методике, а полную замену делать поэтапно, с тестовым окном отката.
Как понять, iptables-legacy или nftables реально фильтрует трафик, если оба присутствуют?
Смотрите iptables --version — там явно (legacy) или (nf_tables). Если это обёртка над nftables, iptables-save и nft list ruleset покажут одни и те же правила в разном синтаксисе. Если классический legacy-бинарник — оба набора существуют параллельно, проверять нужно оба.
Правило с нулевым счётчиком пакетов точно можно удалять?
Не обязательно. Ноль совпадений может означать, что правило защищает от ещё не случившегося события или относится к почти неиспользуемому IPv6. Прежде чем удалять — сверьтесь с логами, а при сомнении временно добавьте логирование вместо удаления.
Что делать, если после аудита я не понимаю смысл трети правил?
Это нормальный результат для сервера без документации. Часть можно оставить как есть, если она не мешает и не создаёт риска — важнее убрать явные дубли и противоречия, а остальное подписать комментариями по мере работы.
Безопасно ли редактировать iptables прямо в проде без тестового стенда?
Если следовать откату по таймеру и держать открытой вторую сессию, риск минимален даже в проде. Тестовый стенд с той же топологией лучше, но для большинства правок его отсутствие не блокирует безопасную работу при дисциплине отката.
Стоит ли переносить пятилетний iptables на nftables при первой же возможности?
Не обязательно срочно. Если правила понятны, задокументированы и производительность устраивает, миграция ради миграции добавляет риск без пользы. Смысл появляется, когда правил действительно много и линейный перебор ощутимо влияет на задержку.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →