Короткий ответ

До nft flush ruleset снимите полный nft list ruleset в файл. Ищите policy drop, счётчики на drop-правилах, hook input/forward/output. Если крутится UFW — его цепочки ufw-*, правьте через ufw, не flush. Смена policy drop на accept «на минуту» оставляет дыру, если сессия оборвалась. Читайте counters: нулевые на вашем allow значат, что пакет не доходит до этой линии (другое правило выше или не тот hook).

Хост host.example (10.0.20.10).

Симптомы и как отличить

  • UFW inactive, трафик всё равно drop — чистый nft/iptables-nft;
  • UFW active — сначала UFW;
  • dmesg: nf_conntrack: table full, dropping packet;
  • SSH timeout при listen 22 — sshd vs filter.
HookКогда
preroutingDNAT, не filter accept
inputк локальным сокетам
forwardтранзит (роутер/docker)
outputс самой машины

Возможные причины

  1. table inet filter policy drop без allow established.
  2. Правило по iifname не тому NIC.
  3. Только ip, клиент ipv6 (table ip vs inet).
  4. conntrack полный — drop новых.
  5. fail2ban nft set.
  6. Docker/ufw конфликт FORWARD.
  7. Админ сделал flush и потом частично восстановил.

Диагностика

sudo nft list ruleset
sudo nft list ruleset > /root/nft-$(date +%F).nft
systemctl is-active ufw nftables
sudo nft list tables

Счётчики (если rules с counter):

sudo nft list ruleset -a

-a даёт handle — для nft delete rule … handle N.

Conntrack:

sudo sysctl net.netfilter.nf_conntrack_count net.netfilter.nf_conntrack_max
sudo conntrack -C 2>/dev/null
dmesg -T | grep -i conntrack | tail

Трассировка (осторожно на проде, коротко):

sudo nft add table inet diag
# не оставляйте trace надолго

Проще: tcpdump -ni ens3 port 5432 — пакеты на NIC есть? Если да, а сокет не принимает — filter/input. Если нет — маршрут/внешний FW.

Журнал nft log prefix:

journalctl -k | grep -i nft | tail

Решение

Сценарий A. Нужен allow established + порт

Идея (если нет UFW, свой table): не копируйте вслепую, сверяйте с существующим:

ct state established,related accept
tcp dport 22 accept
tcp dport 5432 ip saddr 10.0.0.0/16 accept

Вставка выше drop. Через nft insert rule inet filter input position 0 … только понимая position.

Сохраните:

sudo nft list ruleset > /etc/nftables.conf
sudo systemctl enable --now nftables

На Ubuntu nftables.service грузит /etc/nftables.conf. Если UFW включён — не ведите второй параллельный filter.

Сценарий B. UFW + nft

Вернитесь в ufw. Ручные правки chain ufw-user-input перезапишутся ufw reload.

Сценарий C. conntrack full

sudo sysctl net.netfilter.nf_conntrack_max
# временное увеличение — после понимания, кто создаёт сессии

Ищите сканер, DNS amplification через NAT, слишком короткий timeout vs слишком длинный. Не max=10M «на всякий» без RAM.

Сценарий D. fail2ban set

sudo nft list set inet f2b-table addr-set-sshd 2>/dev/null
sudo fail2ban-client status sshd

Unban свой IP, не flush всей таблицы.

Сценарий E. Нужен временный accept для теста

С консоли BMC, узкое правило source=ваш IP, не policy accept на input. Запишите revert в заявку.

Как читать counters и не спутать hook

Правило без counter не докажет hit. Для диагностики временно добавьте counter на drop и на allow порта, затем nft list ruleset. Рост drop при вашем nc = вы попали в это правило. Нулевой allow + нулевой drop = пакет не в этом hook (смотрите forward vs input).

Docker публикует DNAT в ip nat + filter forward. nft list table ip nat обязателен, если порт «открыт» в приложении внутри контейнера. UFW без DEFAULT_FORWARD_POLICY=ACCEPT и без правил docker режет published ports — это не баг sshd.

nf_conntrack_tcp_timeout_established по умолчанию дни; проблема чаще table full из-за коротких соединений (антискан, DNS). Смотрите conntrack -L | awk '{print $1}' | sort | uniq -c.

Сохранение /etc/nftables.conf без flush в начале файла при nft -f добавляет дубликаты правил. Типичный файл начинается с flush ruleset — на хосте с Docker это опасно. Либо UFW, либо свой набор с осторожным flush только своих table.

nft monitor trace включает трассировку каждого пакета — на 10G трафике это DoS самим собой. Для точечной проверки используйте meta nftrace set 1 на узком правиле source IP администратора, затем выключите.

IPv4-only table ip filter не видит IPv6. Dual-stack сервис слушает :: — клиент ходит в inet6, ваши ip-правила молчат, пакет принимается политикой другой table или наоборот drop в ip6. Всегда nft list tables целиком.

Состояние ct state invalid drop полезно, но ломает ASYMMETRIC routing. Если пакеты уходят одним путём, возвращаются другим — conntrack invalid. Это сеть, не «открыть порт».

Как проверить, что проблема устранена

sudo nft list ruleset | head
nc -vz 10.0.20.10 5432
ssh admin@10.0.20.10 true
sudo conntrack -C

Счётчик allow-правила растёт при тесте. policy drop на input сохранён, если такова политика.

Если не помогло

  • tcpdump пуст: апстрим, не nft.
  • Только Docker: iptables -t nat -L/nft nat, published ports.
  • Логи nft забили диск — journald + rate limit log rules.

Профилактика

  • Один оркестратор FW: UFW или nftables.service + ansible.
  • Бэкап ruleset в git.
  • Мониторинг conntrack_count.
  • Комментарии в правилах (comment "pgsql lan").

FAQ

iptables-legacy vs nft?

iptables -V покажет nf_tables. Два стека сразу — классический способ «правило не сработало».

Чем inet лучше ip?

Один table для v4+v6. Не забудьте dport правила покрывают оба, если inet.

nft monitor?

Поток событий. Для учёбы, не круглосуточно без фильтра.

Можно ли policy accept на filter input в проде?

Только если есть явные drop и вы понимаете default. Secure-by-default — drop + allow.

Docker ставит свои chains при reboot

Да. Применяйте свои правила так, чтобы не конфликтовать, или документируйте порядок After=docker.