nftables: проверка правил firewall в изолированном netns без риска потерять доступ

Правила firewall опасны тем, что ошибка проявляется как потеря доступа к серверу, а исправить её можно только с консоли. Проверять правила сразу на рабочей машине рискованно, а чтение конфигурации глазами не гарантирует, что разрешённое действительно работает, а запрещённое запрещено. Решение: изолированное сетевое пространство имён (network namespace, netns). В нём можно создать «сервер» и «клиента», применить правила, послать реальные соединения и посмотреть результат, не затрагивая сеть хоста. Ниже набор правил, скрипт проверки и порядок переноса на рабочий сервер.
Статья опирается на логику направления «Сетевая безопасность» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Проверка выполнена на Debian 12, nftables 1.0.6, ядро 6.1: скрипт выполнялся на сервере, после него не осталось ни пространств имён, ни виртуальных интерфейсов, а правила хоста не менялись. Основы nftables: firewall в Linux.
Что должно получиться
| Уровень | Что это значит на практике |
|---|---|
| 2. Гигиенический | Сеть сегментирована, правила firewall пересматриваются и проверяются, а не только пишутся |
Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.
Идея: два пространства имён и виртуальный кабель
Сетевое пространство имён — изолированный набор интерфейсов, таблиц маршрутизации и правил firewall. Правила, загруженные внутри пространства, не влияют на хост. Схема проверки: пространство zsrv (проверяемый «сервер» с адресом 10.99.0.1), пространство zcli (клиент 10.99.0.2) и пара виртуальных интерфейсов veth между ними. На «сервере» запускаются простые слушатели на портах 22, 80 и 8080, на нём же применяются правила; «клиент» пытается подключиться.
Набор правил
Файл /etc/nftables.conf: политика «запрещено всё, кроме разрешённого», лимиты и набор заблокированных адресов, изменяемый на ходу.
#!/usr/sbin/nft -f
# /etc/nftables.conf: пример набора правил для сервера (политика «запрещено всё, кроме разрешённого»)
flush ruleset
table inet filter {
# адреса и сети, с которых соединения запрещены (изменяется на ходу командой nft add/delete element)
set blocked4 {
type ipv4_addr
flags interval
elements = { 198.51.100.0/24 }
}
chain input {
type filter hook input priority 0; policy drop;
iif lo accept
ct state invalid drop
ct state established,related accept
ip saddr @blocked4 counter drop
# ping: не чаще 5 в секунду
icmp type echo-request limit rate 5/second burst 10 packets accept
# SSH: не более 3 новых соединений в минуту с одного адреса (ограничение подбора)
tcp dport 22 ct state new meter ssh-new { ip saddr limit rate 3/minute burst 3 packets } accept
# веб
tcp dport { 80, 443 } accept
# всё остальное считаем и отбрасываем по политике
counter
}
chain forward {
type filter hook forward priority 0; policy drop;
}
chain output {
type filter hook output priority 0; policy accept;
}
}
| Правило | Что делает |
|---|---|
policy drop в цепочке input |
Всё входящее запрещено, если не разрешено явно |
ct state established,related accept |
Разрешает ответы на исходящие соединения сервера |
ip saddr @blocked4 counter drop |
Отбрасывает пакеты от адресов из набора и считает их |
icmp ... limit rate 5/second |
Ограничивает частоту ping |
meter ssh-new { ip saddr limit rate 3/minute burst 3 packets } |
Не более трёх новых соединений в минуту с одного адреса на порт 22 (замедляет подбор пароля) |
tcp dport { 80, 443 } accept |
Разрешает веб |
итоговый counter |
Считает отбрасываемые пакеты политикой по умолчанию |
Порт 8080 в наборе намеренно не разрешён: в тесте это «служба, которая не должна быть доступна снаружи», и политика по умолчанию должна её закрыть.
Скрипт проверки
Запускайте от root. Скрипт создаёт пространства и интерфейсы с именами zsrv, zcli и zveth0/1, а в конце, в том числе при ошибке, удаляет их и останавливает слушатели. Файл с правилами положите рядом как /tmp/nft_ruleset.nft.
#!/bin/bash
set -u
NS_S=zsrv; NS_C=zcli
cleanup() { ip netns pids $NS_S 2>/dev/null | xargs -r kill 2>/dev/null; ip netns del $NS_S 2>/dev/null; ip netns del $NS_C 2>/dev/null; }
trap cleanup EXIT
cleanup
ip netns add $NS_S; ip netns add $NS_C
ip link add zveth0 type veth peer name zveth1
ip link set zveth0 netns $NS_S; ip link set zveth1 netns $NS_C
ip -n $NS_S addr add 10.99.0.1/24 dev zveth0; ip -n $NS_S link set zveth0 up; ip -n $NS_S link set lo up
ip -n $NS_C addr add 10.99.0.2/24 dev zveth1; ip -n $NS_C link set zveth1 up
for p in 22 80 8080; do ip netns exec $NS_S python3 -m http.server $p --bind 10.99.0.1 >/dev/null 2>&1 & done
sleep 1
echo "=== без правил"
ip netns exec $NS_C curl -s -o /dev/null -m 2 -w "port 8080 http=%{http_code}\n" http://10.99.0.1:8080/
echo "=== проверка синтаксиса"
ip netns exec $NS_S nft -c -f /tmp/nft_ruleset.nft && echo syntax-ok
echo "=== применяем правила в netns сервера"
ip netns exec $NS_S nft -f /tmp/nft_ruleset.nft && echo applied
sleep 1
echo "--- 80 (разрешён)"; ip netns exec $NS_C curl -s -o /dev/null -m 3 -w "http=%{http_code}\n" http://10.99.0.1:80/
echo "--- 8080 (не разрешён, ожидаем таймаут)"; ip netns exec $NS_C curl -s -o /dev/null -m 2 -w "http=%{http_code}\n" http://10.99.0.1:8080/; echo "curl exit=$? (28 = таймаут)"
echo "--- ping x3"; ip netns exec $NS_C ping -c 3 -W 1 -q 10.99.0.1 | tail -2
echo "--- SSH-порт: 6 быстрых новых соединений подряд"
ok=0; fail=0; for i in 1 2 3 4 5 6; do if ip netns exec $NS_C curl -s -o /dev/null -m 1 http://10.99.0.1:22/; then ok=$((ok+1)); else fail=$((fail+1)); fi; done; echo "успешных=$ok отброшенных=$fail"
echo "--- блокировка адреса клиента через набор"
ip netns exec $NS_S nft add element inet filter blocked4 { 10.99.0.2 }
ip netns exec $NS_C curl -s -o /dev/null -m 2 -w "80 после блокировки: http=%{http_code}\n" http://10.99.0.1:80/; echo "curl exit=$?"
ip netns exec $NS_S nft delete element inet filter blocked4 { 10.99.0.2 }
ip netns exec $NS_C curl -s -o /dev/null -m 3 -w "80 после снятия блокировки: http=%{http_code}\n" http://10.99.0.1:80/
echo "--- счётчики"
ip netns exec $NS_S nft list ruleset | grep -E "counter|elements"
echo "--- набор"
ip netns exec $NS_S nft list set inet filter blocked4
Ожидаемый результат
| Проверка | Что должно быть |
|---|---|
| Порт 8080 без правил | http=200 (служба работает) |
nft -c -f |
syntax-ok: синтаксис правильный |
| Порт 80 после применения правил | http=200 |
| Порт 8080 после применения правил | http=000, код curl 28 (таймаут): пакеты отброшены молча |
| Ping 3 раза | 0% потерь (лимит 5 в секунду не превышен) |
| 6 быстрых соединений на порт 22 | успешных 3, отброшенных 3: сработало ограничение |
Адрес клиента добавлен в blocked4 |
порт 80 даёт таймаут; после nft delete element снова http=200 |
nft list ruleset |
Счётчики: у правила блокировки 2 пакета, у итогового правила 5 (это отброшенные запросы к 8080) |
Именно так выглядел вывод на проверке. Если что-то отличается, ошибка в правилах, и вы нашли её без вреда для сервера.
Перенос на рабочий сервер без потери доступа
- Откройте запасную сессию SSH и не закрывайте её. Подготовьте доступ с консоли гипервизора.
- Убедитесь, что порт SSH разрешён в вашем наборе правил и что порт совпадает с настоящим (в примере 22). Если вы подключаетесь с адреса, который не должен попадать под лимит, добавьте разрешение для адресов администраторов до правила с лимитом.
- Проверьте синтаксис:
sudo nft -c -f /etc/nftables.conf. - Сохраните текущие правила:
sudo nft list ruleset > /root/nft.before. - Примените с автоматическим откатом. Запланируйте возврат прежних правил через две минуты, затем примените новые:
sudo systemd-run --on-active=2min --unit=nft-rollback /usr/sbin/nft -f /root/nft.before sudo nft -f /etc/nftables.confЕсли всё работает и вы остались подключены, отмените откат:
sudo systemctl stop nft-rollback.timer. - Включите автозагрузку:
sudo systemctl enable --now nftables(служба читает/etc/nftables.conf). - Проверьте извне реальные порты по плану тестов: что открыто, то работает, что закрыто, не отвечает.
Оформляйте изменение по процессу: карточка, проверка и откат. Помните про Docker: порты, опубликованные контейнерами, обходят эти правила: ограничения контейнеров Docker.
Управление набором на ходу
sudo nft add element inet filter blocked4 { 203.0.113.50 }
sudo nft delete element inet filter blocked4 { 203.0.113.50 }
sudo nft list set inet filter blocked4
Набор позволяет блокировать адреса без перезагрузки правил: его можно наполнять автоматикой, например из CrowdSec и Fail2ban. Добавленные таким образом элементы пропадают после перезагрузки, если не записать их в файл правил.
Диагностика
| Симптом | Причина и что делать |
|---|---|
| Скрипт не создаёт пространства имён | Нет прав root или не установлен пакет iproute2. Запускайте от root, проверьте команду ip netns |
nft -c сообщает об ошибке синтаксиса |
Исправьте указанную строку; для набора с flags interval элементы с диапазоном должны быть в формате сети или диапазона |
| Все проверки дают таймаут | Не разрешён ct state established или слушатели не запущены. Проверьте nft list ruleset и ss -ltn в пространстве сервера |
| Лимит SSH срабатывает слишком рано на рабочем сервере | Один адрес (общий NAT) создаёт много соединений. Увеличьте burst или разрешите адреса администраторов выше правила |
| После применения пропал доступ | Откройте запасную сессию или консоль; сработает автоматический откат; проверьте разрешение порта SSH |
Где это в модели зрелости
Материал относится к направлению «Сетевая безопасность» (материалы направления, карта зрелости). Уровень 2 требует, чтобы правила firewall пересматривались и контролировались: воспроизводимая проверка правил в изолированной среде делает пересмотр безопасным.
Как проверить
- Скрипт проверки выполняется без ошибок, ожидаемые результаты совпадают с таблицей.
- После скрипта на хосте нет остатков:
ip netns listпуст, интерфейсовzveth*нет. - Правила на рабочем сервере применены с откатом, порты проверены извне.
- Набор правил хранится в репозитории или журнале изменений, проверка выполняется при каждом изменении.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
- Документация nftables (Wiki проекта netfilter): wiki.nftables.org; руководство
man ip-netns. - NIST SP 800-41 Rev. 1, Guidelines on Firewalls and Firewall Policy: csrc.nist.gov. Международная практика, не российский норматив.






















